WebUI tests: Extend user group tests with more scenarios

1) Extended webui group automation test with below scenarios
	Scenarios
	 *Add user group with invalid names
	 *Add multiple groups records at one shot
	 *Select and delete multiple records
	 *Find and delete records etc...
2) Improved add_record method to support additional use cases:
	 *confirm by additional buttons: 'Add', 'Add and add another', 'Add and Edit,' 'Cancel'
	 *add multiple records in one call (uses 'Add and add another' behavior)

https://pagure.io/freeipa/issue/7485

Signed-off-by: Varun Mylaraiah <mvarun@redhat.com>
Reviewed-By: Petr Vobornik <pvoborni@redhat.com>
Reviewed-By: Michal Reznik <mreznik@redhat.com>
This commit is contained in:
Varun Mylaraiah 2018-04-05 13:21:51 +05:30 committed by Michal Reznik
parent b43c2f8ab4
commit 105d7d7f2e
3 changed files with 274 additions and 68 deletions

View File

@ -78,3 +78,41 @@ DATA6 = {
('textbox', 'gidnumber', '77777'),
]
}
PKEY7 = ''
DATA7 = {
'pkey': PKEY7,
'add': [
('textbox', 'cn', PKEY7),
('textarea', 'description', 'Empty Group name'),
]
}
PKEY8 = ';test-gr@up'
DATA8 = {
'pkey': PKEY8,
'add': [
('textbox', 'cn', PKEY8),
('textarea', 'description', 'Invalid Group name'),
]
}
PKEY9 = 'itest-group9'
DATA9 = {
'pkey': PKEY9,
'add': [
('textbox', 'cn', PKEY9),
('textarea', 'description', 'test-group9 desc'),
('radio', 'type', 'nonposix'),
]
}
PKEY10 = 'itest-group10'
DATA10 = {
'pkey': PKEY10,
'add': [
('textbox', 'cn', PKEY10),
('textarea', 'description', 'test-group10 desc'),
('radio', 'type', 'nonposix'),
]
}

View File

@ -31,6 +31,12 @@ import ipatests.test_webui.test_rbac as rbac
import ipatests.test_webui.data_sudo as sudo
import pytest
try:
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
except ImportError:
pass
@pytest.mark.tier1
class test_group(UI_driver):
@ -75,6 +81,118 @@ class test_group(UI_driver):
def check_posix_enabled(self, enabled):
self.assert_disabled("[name=gidnumber]", negative=enabled)
@screenshot
def test_add_group_negative(self):
"""
Negative test for adding groups
"""
self.init_app()
self.empty_group_name()
self.invalid_group_name()
self.duplicate_group_name()
self.tailing_spaces_in_group_description()
self.leading_spaces_in_group_description()
def empty_group_name(self):
self.navigate_to_entity(group.ENTITY)
self.facet_button_click('add')
self.dialog_button_click('add')
elem = self.find(".widget[name='cn']")
self.assert_field_validation_required(elem)
self.dialog_button_click('cancel')
def invalid_group_name(self):
expected_error = 'may only include letters, numbers, _, -, . and $'
pkey = ';test-gr@up'
self.navigate_to_entity(group.ENTITY)
self.facet_button_click('add')
self.fill_input('cn', pkey)
elem = self.find(".widget[name='cn']")
self.assert_field_validation(expected_error, parent=elem)
self.dialog_button_click('cancel')
def duplicate_group_name(self):
pkey = 'editors'
expected_error = 'group with name "editors" already exists'
self.navigate_to_entity(group.ENTITY)
self.facet_button_click('add')
self.fill_input('cn', pkey)
self.cancel_retry_dialog(expected_error)
def tailing_spaces_in_group_description(self):
pkey = 'itest_group0'
desc = 'with_trailing_space '
expected_error = 'invalid \'desc\': Leading and trailing ' \
'spaces are not allowed'
self.navigate_to_entity(group.ENTITY)
self.facet_button_click('add')
self.fill_input('cn', pkey)
self.fill_textarea('description', desc)
self.cancel_retry_dialog(expected_error)
def leading_spaces_in_group_description(self):
pkey = 'itest_group0'
desc = ' with_leading_space'
expected_error = 'invalid \'desc\': Leading and trailing' \
' spaces are not allowed'
self.navigate_to_entity(group.ENTITY)
self.facet_button_click('add')
self.fill_input('cn', pkey)
self.fill_textarea('description', desc)
self.cancel_retry_dialog(expected_error)
def cancel_retry_dialog(self, expected_error):
self.dialog_button_click('add')
dialog = self.get_last_error_dialog()
assert (expected_error in dialog.text)
self.wait_for_request()
# Key press for Retry
actions = ActionChains(self.driver)
actions.send_keys(Keys.ENTER).perform()
self.wait_for_request(n=2)
self.dialog_button_click('cancel')
self.wait_for_request(n=2)
self.dialog_button_click('cancel')
@screenshot
def test_add_multiple_group(self):
"""
Use 'add and add another' button to create multiple groups at one shot
"""
self.init_app()
# adding a POSIX and a Non-POSIX group
self.add_record(group.ENTITY, [group.DATA, group.DATA2])
# adding Two Non-POSIX groups
self.add_record(group.ENTITY, [group.DATA9, group.DATA10])
# adding Two POSIX groups
self.add_record(group.ENTITY, [group.DATA5, group.DATA6])
# delete multiple records
records = [group.DATA, group.DATA2, group.DATA5, group.DATA6,
group.DATA9, group.DATA10]
self.select_multiple_records(records)
self.facet_button_click('remove')
self.dialog_button_click('ok')
@screenshot
def test_add_and_edit_group(self):
"""
1. add and switch to edit mode
2. add and cancel
"""
self.init_app()
# add and edit record
self.add_record(group.ENTITY, group.DATA, dialog_btn='add_and_edit')
self.switch_to_facet('details')
self.delete_action()
# add then cancel
self.add_record(group.ENTITY, group.DATA, dialog_btn='cancel')
@screenshot
def test_actions(self):
"""
@ -124,11 +242,9 @@ class test_group(UI_driver):
# prepare
# -------
self.add_record(group.ENTITY, group.DATA)
self.add_record(group.ENTITY, group.DATA2, navigate=False)
self.add_record(group.ENTITY, group.DATA3, navigate=False)
self.add_record(user.ENTITY, user.DATA)
self.add_record(netgroup.ENTITY, netgroup.DATA)
self.add_record(group.ENTITY, [group.DATA, group.DATA2, group.DATA3])
self.add_record(user.ENTITY, [user.DATA, user.DATA2])
self.add_record(netgroup.ENTITY, [netgroup.DATA, netgroup.DATA2])
self.add_record(rbac.ROLE_ENTITY, rbac.ROLE_DATA)
self.add_record(hbac.RULE_ENTITY, hbac.RULE_DATA)
self.add_record(sudo.RULE_ENTITY, sudo.RULE_DATA)
@ -137,24 +253,32 @@ class test_group(UI_driver):
# -------------------------
self.navigate_to_record(group.PKEY, entity=group.ENTITY)
# members
self.add_associations([group.PKEY2], facet='member_group', delete=True)
self.add_associations([user.PKEY], facet='member_user', delete=True)
# "members" add with multiple select
self.add_associations([group.PKEY2, group.PKEY3], facet='member_group',
delete=True)
self.add_associations([user.PKEY, user.PKEY2], facet='member_user',
delete=True)
# TODO: external
# member of
self.add_associations([group.PKEY3], facet='memberof_group', delete=True)
self.add_associations([netgroup.PKEY], facet='memberof_netgroup', delete=True)
self.add_associations([rbac.ROLE_PKEY], facet='memberof_role', delete=True)
self.add_associations([hbac.RULE_PKEY], facet='memberof_hbacrule', delete=True)
# "member of": add with search
self.add_associations([group.PKEY3, group.PKEY2],
facet='memberof_group', delete=True, search=True)
self.add_associations([netgroup.PKEY, netgroup.PKEY2],
facet='memberof_netgroup',
delete=True, search=True)
self.add_associations([rbac.ROLE_PKEY], facet='memberof_role',
delete=True)
self.add_associations([hbac.RULE_PKEY], facet='memberof_hbacrule',
delete=True)
self.navigate_to_record(group.PKEY, entity=group.ENTITY)
self.add_associations([sudo.RULE_PKEY], facet='memberof_sudorule', delete=True)
self.add_associations([sudo.RULE_PKEY], facet='memberof_sudorule',
delete=True, search=True)
# cleanup
# -------
self.delete(group.ENTITY, [group.DATA, group.DATA2, group.DATA3])
self.delete(user.ENTITY, [user.DATA])
self.delete(netgroup.ENTITY, [netgroup.DATA])
self.delete(user.ENTITY, [user.DATA, user.DATA2])
self.delete(netgroup.ENTITY, [netgroup.DATA, netgroup.DATA2])
self.delete(rbac.ROLE_ENTITY, [rbac.ROLE_DATA])
self.delete(hbac.RULE_ENTITY, [hbac.RULE_DATA])
self.delete(sudo.RULE_ENTITY, [sudo.RULE_DATA])
@ -168,11 +292,8 @@ class test_group(UI_driver):
# add
# ---
self.add_record(group.ENTITY, group.DATA)
self.add_record(group.ENTITY, group.DATA2, navigate=False)
self.add_record(group.ENTITY, group.DATA3, navigate=False)
self.add_record(group.ENTITY, group.DATA4, navigate=False)
self.add_record(group.ENTITY, group.DATA5, navigate=False)
self.add_record(group.ENTITY, [group.DATA, group.DATA2, group.DATA3,
group.DATA4, group.DATA5])
self.add_record(user.ENTITY, user.DATA)
# prepare indirect member
@ -215,15 +336,21 @@ class test_group(UI_driver):
self.assert_indirect_record(user.PKEY, group.ENTITY, 'member_user')
self.assert_indirect_record(group.PKEY3, group.ENTITY, 'member_group')
self.assert_indirect_record(group.PKEY5, group.ENTITY, 'memberof_group')
self.assert_indirect_record(netgroup.PKEY, group.ENTITY, 'memberof_netgroup')
self.assert_indirect_record(rbac.ROLE_PKEY, group.ENTITY, 'memberof_role')
self.assert_indirect_record(hbac.RULE_PKEY, group.ENTITY, 'memberof_hbacrule')
self.assert_indirect_record(sudo.RULE_PKEY, group.ENTITY, 'memberof_sudorule')
self.assert_indirect_record(group.PKEY5, group.ENTITY,
'memberof_group')
self.assert_indirect_record(netgroup.PKEY, group.ENTITY,
'memberof_netgroup')
self.assert_indirect_record(rbac.ROLE_PKEY, group.ENTITY,
'memberof_role')
self.assert_indirect_record(hbac.RULE_PKEY, group.ENTITY,
'memberof_hbacrule')
self.assert_indirect_record(sudo.RULE_PKEY, group.ENTITY,
'memberof_sudorule')
## cleanup
## -------
self.delete(group.ENTITY, [group.DATA, group.DATA2, group.DATA3, group.DATA4, group.DATA5])
# cleanup
# -------
self.delete(group.ENTITY, [group.DATA, group.DATA2, group.DATA3,
group.DATA4, group.DATA5])
self.delete(user.ENTITY, [user.DATA])
self.delete(netgroup.ENTITY, [netgroup.DATA])
self.delete(rbac.ROLE_ENTITY, [rbac.ROLE_DATA])

View File

@ -1268,9 +1268,9 @@ class UI_driver(object):
self.wait_for_request(n=2)
def add_record(self, entity, data, facet='search', facet_btn='add',
dialog_btn='add', delete=False, pre_delete=True,
dialog_name='add', navigate=True, combobox_input=None,
negative=False):
dialog_btn='add', add_another_btn='add_and_add_another',
delete=False, pre_delete=True, dialog_name='add',
navigate=True, combobox_input=None, negative=False):
"""
Add records.
@ -1285,8 +1285,15 @@ class UI_driver(object):
],
}
"""
pkey = data['pkey']
if type(data) is not list:
data = [data]
last_element = data[len(data) - 1]
pkeys = []
for record in data:
pkeys.append(record['pkey'])
if navigate:
self.navigate_to_entity(entity, facet)
@ -1294,8 +1301,9 @@ class UI_driver(object):
self.assert_facet(entity, facet)
# delete if exists, ie. from previous test fail
if pre_delete:
self.delete_record(pkey, data.get('del'))
self.delete(entity, data, navigate=False)
# current row count
self.wait_for_request(0.5)
@ -1306,40 +1314,58 @@ class UI_driver(object):
self.facet_button_click(facet_btn)
self.assert_dialog(dialog_name)
# fill dialog
self.fill_fields(data['add'], combobox_input=combobox_input)
for record in data:
# confirm dialog
self.dialog_button_click(dialog_btn)
self.wait_for_request()
self.wait_for_request()
# fill dialog
self.fill_fields(record['add'], combobox_input=combobox_input)
# check expected error/warning/info
expected = ['error_4304_info']
dialog_info = self.get_dialog_info()
if dialog_info and dialog_info['name'] in expected:
self.dialog_button_click('ok')
btn = dialog_btn
if record != last_element:
btn = add_another_btn
self.dialog_button_click(btn)
self.wait_for_request()
self.wait_for_request()
if negative:
# check expected error/warning/info
expected = ['error_4304_info']
dialog_info = self.get_dialog_info()
if dialog_info and dialog_info['name'] in expected:
self.dialog_button_click('ok')
self.wait_for_request()
if negative:
return
# check for error
self.assert_no_error_dialog()
self.wait_for_request()
self.wait_for_request(0.4)
if dialog_btn == 'add_and_edit':
page_pkey = self.get_text('.facet-pkey')
assert record['pkey'] in page_pkey
# we cannot delete because we are on different page
return
# check for error
self.assert_no_error_dialog()
self.wait_for_request()
self.wait_for_request(0.4)
# check if table has more rows
elif dialog_btn == add_another_btn:
# dialog is still open, we cannot check for records on search page
# or delete the records
return
elif dialog_btn == 'cancel':
return
# when standard 'add' was used then it will land on search page
# and we can check if new item was added - table has more rows
new_count = len(self.get_rows())
# adjust because of paging
expected = count + 1
expected = count + len(data)
if count == 20:
expected = 20
self.assert_row_count(expected, new_count)
# delete record
if delete:
self.delete_record(pkey)
self.delete(entity, data, navigate=False)
new_count = len(self.get_rows())
self.assert_row_count(count, new_count)
@ -1403,10 +1429,9 @@ class UI_driver(object):
self.wait_for_request()
# 2. Add record
self.add_record(parent_entity, data, facet=search_facet, navigate=False,
facet_btn=add_facet_btn, dialog_name=add_dialog_name,
dialog_btn=add_dialog_btn
)
self.add_record(parent_entity, data, facet=search_facet,
navigate=False, facet_btn=add_facet_btn,
dialog_name=add_dialog_name, dialog_btn=add_dialog_btn)
self.close_notifications()
@ -1458,7 +1483,7 @@ class UI_driver(object):
def prepare_associations(
self, pkeys, facet=None, facet_btn='add', member_pkeys=None,
confirm_btn='add'):
confirm_btn='add', search=False):
"""
Helper function for add_associations and delete_associations
"""
@ -1469,8 +1494,18 @@ class UI_driver(object):
self.wait()
self.wait_for_request()
for key in pkeys:
self.select_record(key, table_name='available')
if search is True:
for key in pkeys:
search_field_s = '.adder-dialog-top input[name="filter"]'
self.fill_text(search_field_s, key)
self._button_click(selector="button[name='find'].btn-default",
parent=None)
self.wait_for_request()
self.select_record(key, table_name='available')
self.button_click('add')
else:
for key in pkeys:
self.select_record(key, table_name='available')
self.button_click('add')
self.dialog_button_click(confirm_btn)
@ -1485,18 +1520,19 @@ class UI_driver(object):
def add_associations(
self, pkeys, facet=None, delete=False, facet_btn='add',
member_pkeys=None, confirm_btn='add'):
member_pkeys=None, confirm_btn='add', search=False):
"""
Add associations
"""
check_pkeys = self.prepare_associations(
pkeys, facet, facet_btn, member_pkeys, confirm_btn=confirm_btn)
pkeys, facet, facet_btn, member_pkeys, confirm_btn, search)
# we need to return if we want to "cancel" to avoid assert record fail
if confirm_btn == 'cancel':
return
for key in check_pkeys:
self.assert_record(key)
if delete:
self.delete_record(key)
@ -1513,7 +1549,8 @@ class UI_driver(object):
for key in check_pkeys:
self.assert_record(key, negative=True)
def add_table_associations(self, table_name, pkeys, parent=False, delete=False):
def add_table_associations(self, table_name, pkeys, parent=False,
delete=False):
"""
Add value to table (association|rule|...)
"""
@ -1987,9 +2024,9 @@ class UI_driver(object):
assert is_enabled == enabled, ('Invalid enabled state of action item %s. '
'Expected: %s') % (action, str(visible))
def assert_field_validation_required(self, parent=None):
def assert_field_validation(self, expect_error, parent=None):
"""
Assert we got 'Required field' error message in field validation
Assert for error in field validation
"""
if not parent:
@ -1998,7 +2035,11 @@ class UI_driver(object):
req_field_css = '.help-block[name="error_link"]'
res = self.find(req_field_css, By.CSS_SELECTOR, context=parent)
assert 'Required field' in res.text, 'No "Required field" error found'
assert expect_error in res.text, \
'Expected error: {} not found'.format(expect_error)
def assert_field_validation_required(self, parent=None):
self.assert_field_validation('Required field', parent)
def assert_notification(self, type='success', assert_text=None):
"""