URL: https://github.com/freeipa/freeipa/pull/1835 Author: Rezney Title: #1835: [Backport][ipa-4-6] WebUI test :: Updated existing test_group Action: opened
PR body: """ This PR was opened automatically because PR #1728 was pushed to master and backport to ipa-4-6 is required. """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/1835/head:pr1835 git checkout pr1835
From 1cf1b30d5e45918e6e70c647b9a5dfdf8bd5fd2b Mon Sep 17 00:00:00 2001 From: Varun Mylaraiah <mva...@redhat.com> Date: Thu, 5 Apr 2018 13:21:51 +0530 Subject: [PATCH 1/2] 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 <mva...@redhat.com> --- ipatests/test_webui/data_group.py | 38 ++++++++ ipatests/test_webui/test_group.py | 185 ++++++++++++++++++++++++++++++++------ ipatests/test_webui/ui_driver.py | 117 ++++++++++++++++-------- 3 files changed, 273 insertions(+), 67 deletions(-) diff --git a/ipatests/test_webui/data_group.py b/ipatests/test_webui/data_group.py index 517f98f04b..7ef4edd390 100644 --- a/ipatests/test_webui/data_group.py +++ b/ipatests/test_webui/data_group.py @@ -78,3 +78,41 @@ ('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'), + ] +} diff --git a/ipatests/test_webui/test_group.py b/ipatests/test_webui/test_group.py index db9acef345..fc1a3a2d97 100644 --- a/ipatests/test_webui/test_group.py +++ b/ipatests/test_webui/test_group.py @@ -31,6 +31,12 @@ 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 @@ def test_group_types(self): 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 @@ def test_associations(self): # 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 @@ def test_associations(self): # ------------------------- 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 @@ def test_indirect_associations(self): # 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 @@ def test_indirect_associations(self): 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]) diff --git a/ipatests/test_webui/ui_driver.py b/ipatests/test_webui/ui_driver.py index c60ef34fab..70fe570352 100644 --- a/ipatests/test_webui/ui_driver.py +++ b/ipatests/test_webui/ui_driver.py @@ -1258,9 +1258,9 @@ def find_record(self, entity, data, facet='search', dummy='XXXXXXX'): 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. @@ -1275,8 +1275,15 @@ def add_record(self, entity, data, facet='search', facet_btn='add', ], } """ - 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) @@ -1284,8 +1291,9 @@ def add_record(self, entity, data, facet='search', facet_btn='add', 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) @@ -1296,40 +1304,58 @@ def add_record(self, entity, data, facet='search', facet_btn='add', 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) + + btn = dialog_btn - # 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') + if record != last_element: + btn = add_another_btn + + self.dialog_button_click(btn) + self.wait_for_request() self.wait_for_request() - if negative: - return + # 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() - # check for error - self.assert_no_error_dialog() - self.wait_for_request() - self.wait_for_request(0.4) + if negative: + 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 + 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 + 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) @@ -1393,10 +1419,9 @@ def basic_crud(self, entity, data, 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() @@ -1448,7 +1473,7 @@ def add_table_record(self, name, data, parent=None): 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 """ @@ -1459,8 +1484,18 @@ def prepare_associations( 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) @@ -1475,18 +1510,19 @@ def prepare_associations( 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) @@ -1503,7 +1539,8 @@ def delete_associations( 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|...) """ @@ -1977,9 +2014,9 @@ def assert_action_list_action(self, action, visible=True, enabled=True, 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: @@ -1988,7 +2025,11 @@ def assert_field_validation_required(self, parent=None): 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): """ From 45b982762225858a643af8c41c967bcfad8d5fcc Mon Sep 17 00:00:00 2001 From: Varun Mylaraiah <mva...@redhat.com> Date: Fri, 13 Apr 2018 16:03:53 +0530 Subject: [PATCH 2/2] Fixed improper clean-up in test_host::test_kerberos_flags added closing the notification in kerberos flags Signed-off-by: Varun Mylaraiah <mva...@redhat.com> --- ipatests/test_webui/test_host.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipatests/test_webui/test_host.py b/ipatests/test_webui/test_host.py index 0fced04235..48714f1f13 100644 --- a/ipatests/test_webui/test_host.py +++ b/ipatests/test_webui/test_host.py @@ -317,7 +317,8 @@ def test_kerberos_flags(self): self.validate_fields([('checkbox', name, checked)]) self.mod_record(ENTITY, mod) self.validate_fields([('checkbox', name, [])]) - self.delete_record(self.pkey, self.data.get('del')) + self.close_notifications() + self.delete(ENTITY, [self.data]) @screenshot def test_associations(self):
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org