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

Reply via email to