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'), ('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 ipatests.test_webui.data_sudo as sudo
import pytest import pytest
try:
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
except ImportError:
pass
@pytest.mark.tier1 @pytest.mark.tier1
class test_group(UI_driver): class test_group(UI_driver):
@ -75,6 +81,118 @@ class test_group(UI_driver):
def check_posix_enabled(self, enabled): def check_posix_enabled(self, enabled):
self.assert_disabled("[name=gidnumber]", negative=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 @screenshot
def test_actions(self): def test_actions(self):
""" """
@ -124,11 +242,9 @@ class test_group(UI_driver):
# prepare # prepare
# ------- # -------
self.add_record(group.ENTITY, group.DATA) self.add_record(group.ENTITY, [group.DATA, group.DATA2, group.DATA3])
self.add_record(group.ENTITY, group.DATA2, navigate=False) self.add_record(user.ENTITY, [user.DATA, user.DATA2])
self.add_record(group.ENTITY, group.DATA3, navigate=False) self.add_record(netgroup.ENTITY, [netgroup.DATA, netgroup.DATA2])
self.add_record(user.ENTITY, user.DATA)
self.add_record(netgroup.ENTITY, netgroup.DATA)
self.add_record(rbac.ROLE_ENTITY, rbac.ROLE_DATA) self.add_record(rbac.ROLE_ENTITY, rbac.ROLE_DATA)
self.add_record(hbac.RULE_ENTITY, hbac.RULE_DATA) self.add_record(hbac.RULE_ENTITY, hbac.RULE_DATA)
self.add_record(sudo.RULE_ENTITY, sudo.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) self.navigate_to_record(group.PKEY, entity=group.ENTITY)
# members # "members" add with multiple select
self.add_associations([group.PKEY2], facet='member_group', delete=True) self.add_associations([group.PKEY2, group.PKEY3], facet='member_group',
self.add_associations([user.PKEY], facet='member_user', delete=True) delete=True)
self.add_associations([user.PKEY, user.PKEY2], facet='member_user',
delete=True)
# TODO: external # TODO: external
# member of # "member of": add with search
self.add_associations([group.PKEY3], facet='memberof_group', delete=True) self.add_associations([group.PKEY3, group.PKEY2],
self.add_associations([netgroup.PKEY], facet='memberof_netgroup', delete=True) facet='memberof_group', delete=True, search=True)
self.add_associations([rbac.ROLE_PKEY], facet='memberof_role', delete=True) self.add_associations([netgroup.PKEY, netgroup.PKEY2],
self.add_associations([hbac.RULE_PKEY], facet='memberof_hbacrule', delete=True) 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.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 # cleanup
# ------- # -------
self.delete(group.ENTITY, [group.DATA, group.DATA2, group.DATA3]) self.delete(group.ENTITY, [group.DATA, group.DATA2, group.DATA3])
self.delete(user.ENTITY, [user.DATA]) self.delete(user.ENTITY, [user.DATA, user.DATA2])
self.delete(netgroup.ENTITY, [netgroup.DATA]) self.delete(netgroup.ENTITY, [netgroup.DATA, netgroup.DATA2])
self.delete(rbac.ROLE_ENTITY, [rbac.ROLE_DATA]) self.delete(rbac.ROLE_ENTITY, [rbac.ROLE_DATA])
self.delete(hbac.RULE_ENTITY, [hbac.RULE_DATA]) self.delete(hbac.RULE_ENTITY, [hbac.RULE_DATA])
self.delete(sudo.RULE_ENTITY, [sudo.RULE_DATA]) self.delete(sudo.RULE_ENTITY, [sudo.RULE_DATA])
@ -168,11 +292,8 @@ class test_group(UI_driver):
# add # add
# --- # ---
self.add_record(group.ENTITY, group.DATA) self.add_record(group.ENTITY, [group.DATA, group.DATA2, group.DATA3,
self.add_record(group.ENTITY, group.DATA2, navigate=False) group.DATA4, group.DATA5])
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(user.ENTITY, user.DATA) self.add_record(user.ENTITY, user.DATA)
# prepare indirect member # 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(user.PKEY, group.ENTITY, 'member_user')
self.assert_indirect_record(group.PKEY3, group.ENTITY, 'member_group') self.assert_indirect_record(group.PKEY3, group.ENTITY, 'member_group')
self.assert_indirect_record(group.PKEY5, group.ENTITY, 'memberof_group') self.assert_indirect_record(group.PKEY5, group.ENTITY,
self.assert_indirect_record(netgroup.PKEY, group.ENTITY, 'memberof_netgroup') 'memberof_group')
self.assert_indirect_record(rbac.ROLE_PKEY, group.ENTITY, 'memberof_role') self.assert_indirect_record(netgroup.PKEY, group.ENTITY,
self.assert_indirect_record(hbac.RULE_PKEY, group.ENTITY, 'memberof_hbacrule') 'memberof_netgroup')
self.assert_indirect_record(sudo.RULE_PKEY, group.ENTITY, 'memberof_sudorule') 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 # cleanup
## ------- # -------
self.delete(group.ENTITY, [group.DATA, group.DATA2, group.DATA3, group.DATA4, group.DATA5]) self.delete(group.ENTITY, [group.DATA, group.DATA2, group.DATA3,
group.DATA4, group.DATA5])
self.delete(user.ENTITY, [user.DATA]) self.delete(user.ENTITY, [user.DATA])
self.delete(netgroup.ENTITY, [netgroup.DATA]) self.delete(netgroup.ENTITY, [netgroup.DATA])
self.delete(rbac.ROLE_ENTITY, [rbac.ROLE_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) self.wait_for_request(n=2)
def add_record(self, entity, data, facet='search', facet_btn='add', def add_record(self, entity, data, facet='search', facet_btn='add',
dialog_btn='add', delete=False, pre_delete=True, dialog_btn='add', add_another_btn='add_and_add_another',
dialog_name='add', navigate=True, combobox_input=None, delete=False, pre_delete=True, dialog_name='add',
negative=False): navigate=True, combobox_input=None, negative=False):
""" """
Add records. 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: if navigate:
self.navigate_to_entity(entity, facet) self.navigate_to_entity(entity, facet)
@ -1294,8 +1301,9 @@ class UI_driver(object):
self.assert_facet(entity, facet) self.assert_facet(entity, facet)
# delete if exists, ie. from previous test fail # delete if exists, ie. from previous test fail
if pre_delete: if pre_delete:
self.delete_record(pkey, data.get('del')) self.delete(entity, data, navigate=False)
# current row count # current row count
self.wait_for_request(0.5) self.wait_for_request(0.5)
@ -1306,40 +1314,58 @@ class UI_driver(object):
self.facet_button_click(facet_btn) self.facet_button_click(facet_btn)
self.assert_dialog(dialog_name) self.assert_dialog(dialog_name)
# fill dialog for record in data:
self.fill_fields(data['add'], combobox_input=combobox_input)
# confirm dialog # fill dialog
self.dialog_button_click(dialog_btn) self.fill_fields(record['add'], combobox_input=combobox_input)
self.wait_for_request()
self.wait_for_request()
# check expected error/warning/info btn = dialog_btn
expected = ['error_4304_info']
dialog_info = self.get_dialog_info() if record != last_element:
if dialog_info and dialog_info['name'] in expected: btn = add_another_btn
self.dialog_button_click('ok')
self.dialog_button_click(btn)
self.wait_for_request()
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 return
elif dialog_btn == add_another_btn:
# check for error # dialog is still open, we cannot check for records on search page
self.assert_no_error_dialog() # or delete the records
self.wait_for_request() return
self.wait_for_request(0.4) elif dialog_btn == 'cancel':
return
# check if table has more rows # 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()) new_count = len(self.get_rows())
# adjust because of paging # adjust because of paging
expected = count + 1 expected = count + len(data)
if count == 20: if count == 20:
expected = 20 expected = 20
self.assert_row_count(expected, new_count) self.assert_row_count(expected, new_count)
# delete record # delete record
if delete: if delete:
self.delete_record(pkey) self.delete(entity, data, navigate=False)
new_count = len(self.get_rows()) new_count = len(self.get_rows())
self.assert_row_count(count, new_count) self.assert_row_count(count, new_count)
@ -1403,10 +1429,9 @@ class UI_driver(object):
self.wait_for_request() self.wait_for_request()
# 2. Add record # 2. Add record
self.add_record(parent_entity, data, facet=search_facet, navigate=False, self.add_record(parent_entity, data, facet=search_facet,
facet_btn=add_facet_btn, dialog_name=add_dialog_name, navigate=False, facet_btn=add_facet_btn,
dialog_btn=add_dialog_btn dialog_name=add_dialog_name, dialog_btn=add_dialog_btn)
)
self.close_notifications() self.close_notifications()
@ -1458,7 +1483,7 @@ class UI_driver(object):
def prepare_associations( def prepare_associations(
self, pkeys, facet=None, facet_btn='add', member_pkeys=None, 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 Helper function for add_associations and delete_associations
""" """
@ -1469,8 +1494,18 @@ class UI_driver(object):
self.wait() self.wait()
self.wait_for_request() self.wait_for_request()
for key in pkeys: if search is True:
self.select_record(key, table_name='available') 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.button_click('add')
self.dialog_button_click(confirm_btn) self.dialog_button_click(confirm_btn)
@ -1485,18 +1520,19 @@ class UI_driver(object):
def add_associations( def add_associations(
self, pkeys, facet=None, delete=False, facet_btn='add', 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 Add associations
""" """
check_pkeys = self.prepare_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 # we need to return if we want to "cancel" to avoid assert record fail
if confirm_btn == 'cancel': if confirm_btn == 'cancel':
return return
for key in check_pkeys: for key in check_pkeys:
self.assert_record(key) self.assert_record(key)
if delete: if delete:
self.delete_record(key) self.delete_record(key)
@ -1513,7 +1549,8 @@ class UI_driver(object):
for key in check_pkeys: for key in check_pkeys:
self.assert_record(key, negative=True) 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|...) 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. ' assert is_enabled == enabled, ('Invalid enabled state of action item %s. '
'Expected: %s') % (action, str(visible)) '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: if not parent:
@ -1998,7 +2035,11 @@ class UI_driver(object):
req_field_css = '.help-block[name="error_link"]' req_field_css = '.help-block[name="error_link"]'
res = self.find(req_field_css, By.CSS_SELECTOR, context=parent) 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): def assert_notification(self, type='success', assert_text=None):
""" """