diff --git a/web/pgadmin/feature_tests/file_manager_test.py b/web/pgadmin/feature_tests/file_manager_test.py index 288dc9838..f90dd130c 100644 --- a/web/pgadmin/feature_tests/file_manager_test.py +++ b/web/pgadmin/feature_tests/file_manager_test.py @@ -67,9 +67,9 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): def _create_new_file(self): self.page.find_by_css_selector(QueryToolLocatorsCss.btn_save).click() # Set the XSS value in input - print('Create file') self.page.find_by_css_selector('.change_file_types') - self.page.fill_input_by_css_selector("#file-input-path", self.XSS_FILE) + self.page.fill_input_by_css_selector("input#file-input-path", + self.XSS_FILE) # Save the file self.page.click_modal('Create') self.page.wait_for_query_tool_loading_indicator_to_disappear() diff --git a/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py b/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py index 739e7cf76..7d59aa150 100644 --- a/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py @@ -53,6 +53,8 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.wait = WebDriverWait(self.page.driver, 20) + test_gui_helper.close_bgprocess_popup(self) + def runTest(self): self.page.toggle_open_server(self.server['name']) self.page.toggle_open_tree_item('Databases') @@ -68,7 +70,6 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, ".file [name='file']"))).click() - # .input-group-append >button self.page.fill_input_by_field_name( "file", "test_backup", loose_focus=True) @@ -77,11 +78,10 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.page.find_by_css_selector('.ajs-bg-bgprocess') - # status = self.page.find_by_css_selector( - # ".pg-bg-status .bg-success-light .pg-bg-status-text").text + status = test_utils.get_watcher_dialogue_status(self) - status = self.page.find_by_css_selector( - ".pg-bg-status-text").text + if status != "Successfully completed.": + test_gui_helper.close_bgprocess_popup(self) self.assertEquals(status, "Successfully completed.") @@ -130,8 +130,11 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.page.find_by_css_selector('.ajs-bg-bgprocess') - status = self.page.find_by_css_selector( - ".pg-bg-status-text").text + status = test_utils.get_watcher_dialogue_status(self) + + if status != "Successfully completed.": + test_gui_helper.close_bgprocess_popup(self) + self.assertEquals(status, "Successfully completed.") self.page.find_by_css_selector( @@ -158,7 +161,6 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): os.remove(backup_file) def after(self): - test_gui_helper.close_bgprocess_popup(self) self.page.remove_server(self.server) connection = test_utils.get_db_connection( self.server['db'], diff --git a/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py b/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py index 09589dd3d..81208f7a2 100644 --- a/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py @@ -61,6 +61,7 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): self.table_name) self.page.add_server(self.server) self.wait = WebDriverWait(self.page.driver, 20) + test_gui_helper.close_bgprocess_popup(self) def runTest(self): self._open_maintenance_dialogue() @@ -75,7 +76,7 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): if self.test_level == 'table': self.page.toggle_open_tree_item('Schemas') self.page.toggle_open_tree_item('public') - self.page.toggle_open_tree_item('Tables') + self.page.toggle_open_tables_node() self.page.select_tree_item(self.table_name) self.driver.find_element_by_link_text("Tools").click() @@ -83,8 +84,10 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): time.sleep(0.5) def _verify_command(self): - status = self.page.find_by_css_selector( - ".pg-bg-status-text").text + status = test_utils.get_watcher_dialogue_status(self) + if status != "Successfully completed.": + test_gui_helper.close_bgprocess_popup(self) + self.assertEquals(status, "Successfully completed.") self.page.find_by_css_selector(".pg-bg-more-details").click() command = self.page.find_by_css_selector( @@ -113,7 +116,6 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): "div.wcFloatingFocus div.fa-close").click() def after(self): - test_gui_helper.close_bgprocess_popup(self) self.page.remove_server(self.server) connection = test_utils.get_db_connection( self.server['db'], diff --git a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py index 4aedb6bf0..3da581fdc 100644 --- a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py +++ b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py @@ -36,6 +36,10 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest): self.server, "postgres", "a_test_function" ) + if test_utils.does_function_exist(self.server, 'postgres', + 'a_test_function') != 'True': + raise Exception("The required function is not found") + def runTest(self): self.page.wait_for_spinner_to_disappear() self.page.add_server(self.server) @@ -53,7 +57,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest): self.page.toggle_open_tree_item('postgres') self.page.toggle_open_tree_item('Schemas') self.page.toggle_open_tree_item('public') - self.page.toggle_open_tree_item('Functions') + self.page.toggle_open_function_node() self.page.select_tree_item("a_test_function()") def _debug_function(self): diff --git a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py b/web/pgadmin/feature_tests/xss_checks_roles_control_test.py index 8a568a98e..2dac1dde2 100644 --- a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py +++ b/web/pgadmin/feature_tests/xss_checks_roles_control_test.py @@ -58,7 +58,8 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): def _check_role_membership_control(self): self.page.driver.find_element_by_link_text("Object").click() self.page.driver.find_element_by_link_text("Properties...").click() - self.page.find_by_partial_link_text("Membership").click() + # self.page.find_by_partial_link_text("Membership").click() + self.click_membership_tab() # Fetch the source code for our custom control source_code = self.page.find_by_xpath( "//div[contains(@class,'rolmembership')]" @@ -78,3 +79,19 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): # For XSS we need to search against element's html code assert source_code.find(string_to_find) != - \ 1, "{0} might be vulnerable to XSS ".format(source) + + def click_membership_tab(self): + """This will click and open membership tab of role""" + success = False + attempts = 3 + while not success and attempts > 0: + membership_tab_link = self.page.find_by_xpath( + "//a[normalize-space(text())='Membership']") + membership_tab_link.click() + try: + self.page.find_by_xpath("//input[@placeholder=" + "'Select members']") + break + except Exception as e: + attempts -= 1 + pass diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index 96f60b379..d633f95dc 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -9,6 +9,7 @@ import time +from selenium import webdriver from selenium.common.exceptions import NoSuchElementException, \ WebDriverException, TimeoutException, NoSuchWindowException, \ StaleElementReferenceException @@ -71,7 +72,8 @@ class PgadminPage: .perform() self.find_by_partial_link_text("Server...").click() - self.fill_input_by_field_name("name", server_config['name']) + self.fill_input_by_field_name("name", server_config['name'], + loose_focus=True) self.find_by_partial_link_text("Connection").click() self.fill_input_by_field_name("host", server_config['host']) self.fill_input_by_field_name("port", server_config['port']) @@ -151,7 +153,18 @@ class PgadminPage: "//*[@id='tree']//*[contains(text(), '" + tree_item_text + "')]" "/parent::span[@class='aciTreeItem']") self.driver.execute_script("arguments[0].scrollIntoView()", item) - item.click() + # unexpected exception like element overlapping, click attempts more + # than one time + attempts = 3 + while attempts > 0: + try: + item.click() + break + except Exception as e: + attempts -= 1 + time.sleep(.4) + if attempts == 0: + raise Exception(e) def toggle_open_servers_group(self): """This will open Servers group to display underlying nodes""" @@ -197,13 +210,66 @@ class PgadminPage: "='Tables']]]/span[@class='aciTreeButton']") ActionChains(self.driver).click(item_button).perform() + def toggle_open_function_node(self): + """The function will be used for opening Functions node only""" + node_expanded = False + attempts = 3 + + xpath_for_functions_node = \ + "//span[@class='aciTreeText' and starts-with(text()," \ + "'Functions')]" + xpath_for_exp = "//div[div[div[div[div[div[div[div[span[span[" \ + "(@class='aciTreeText') and starts-with(text()," \ + "'Functions')]]]]]]]]]]" + xpath_for_button = "//div[span[span[(@class='aciTreeText') " \ + "and starts-with(text(),'Functions')]]]" \ + "/span[@class='aciTreeButton']" + + while node_expanded is not True and attempts > 0: + # get the element which contains 'aria-expanded' info + + xpath_for_refresh_btn = "//li[@class='context-menu-item']" \ + "/span[text()='Refresh...']" + + # add code to refresh button, sometime the the collapsing button + # is not visible even if there is sub node. + functions_node_ele = self.find_by_xpath(xpath_for_functions_node) + + webdriver.ActionChains(self.driver).move_to_element( + functions_node_ele).context_click().perform() + refresh_btn = self.find_by_xpath(xpath_for_refresh_btn) + refresh_btn.click() + time.sleep(.5) + + # get the expansion status + function_expansion_ele = self.find_by_xpath(xpath_for_exp) + + # look into the attribute and check if it is already expanded or + # not + if function_expansion_ele.get_attribute('aria-expanded') \ + == 'false': + # button element of the Function node to open it + item_button = self.find_by_xpath(xpath_for_button) + ActionChains(self.driver).click(item_button).perform() + # Expansion of element on GUI takes sometime, so put small + # sleep + time.sleep(.5) + function_expansion_ele = self.find_by_xpath( + xpath_for_exp) + if function_expansion_ele.get_attribute('aria-expanded') \ + == 'true': + break + else: + attempts -= 1 + else: + node_expanded = True + def toggle_open_server(self, tree_item_text): def check_for_password_dialog_or_tree_open(driver): try: dialog = driver.find_element_by_id("frmPassword") except WebDriverException: dialog = None - try: database_node = driver.find_element_by_xpath( "//*[@id='tree']//*[.='Databases']" @@ -284,8 +350,15 @@ class PgadminPage: def fill_input(self, field, field_content, input_keys=False, key_after_input=Keys.ARROW_DOWN): - field.click() - + try: + attempt = 0 + for attempt in range(0, 3): + field.click() + break + except Exception as e: + time.sleep(.2) + if attempt == 2: + raise Exception(e) # Use send keys if input_keys true, else use javascript to set content if input_keys: backspaces = [Keys.BACKSPACE] * len(field.get_attribute('value')) diff --git a/web/regression/python_test_utils/test_gui_helper.py b/web/regression/python_test_utils/test_gui_helper.py index 2703cc8af..5174492e2 100644 --- a/web/regression/python_test_utils/test_gui_helper.py +++ b/web/regression/python_test_utils/test_gui_helper.py @@ -10,14 +10,15 @@ def close_bgprocess_popup(tester): """ - Allow us to close the background process popup window + Allows us to close the background process popup window """ - tester._screenshot() - + screen_shot_taken = False # In cases where backup div is not closed (sometime due to some error) try: if tester.driver.find_element_by_css_selector( ".ajs-message.ajs-bg-bgprocess.ajs-visible"): + tester._screenshot() + screen_shot_taken = True tester.driver.find_element_by_css_selector( ".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click() except Exception: @@ -28,6 +29,8 @@ def close_bgprocess_popup(tester): if tester.driver.find_element_by_xpath( "//div[@class='card-header bg-primary d-flex']/div" "[contains(text(), 'Restoring backup')]"): + tester._screenshot() + screen_shot_taken = True tester.driver.find_element_by_css_selector( ".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click() except Exception: @@ -39,7 +42,12 @@ def close_bgprocess_popup(tester): if tester.driver.find_element_by_xpath( "//div[@class='card-header bg-primary d-flex']/div" "[contains(text(), 'Maintenance')]"): + tester._screenshot() + screen_shot_taken = True tester.driver.find_element_by_css_selector( ".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click() except Exception: pass + + if not screen_shot_taken: + tester._screenshot() diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py index 786cab0a7..962b338b9 100644 --- a/web/regression/python_test_utils/test_utils.py +++ b/web/regression/python_test_utils/test_utils.py @@ -319,6 +319,26 @@ def drop_debug_function(server, db_name, function_name="test_func"): traceback.print_exc(file=sys.stderr) +def does_function_exist(server, db_name, fun_name): + query = "select exists(select * " \ + "from pg_proc where proname = '%s');" % fun_name + + connection = get_db_connection( + db_name, + server['username'], + server['db_password'], + server['host'], + server['port'], + server['sslmode'] + ) + + cursor = connection.cursor() + + cursor.execute(query) + result = cursor.fetchall() + return str(result[0][0]) + + def create_role(server, db_name, role_name="test_role"): try: connection = get_db_connection( @@ -1006,3 +1026,22 @@ def check_binary_path_or_skip_test(cls, utility_name): retVal = is_utility_exists(binary_path) if retVal is not None: cls.skipTest(retVal) + + +def get_watcher_dialogue_status(self): + """This will get watcher dialogue status""" + import time + attempts = 120 + + while attempts > 0: + status = self.page.find_by_css_selector( + ".pg-bg-status-text").text + + if 'Failed' in status: + break + if status == 'Started' or status == 'Running...': + attempts -= 1 + time.sleep(.5) + else: + break + return status