diff --git a/docs/en_US/release_notes_4_13.rst b/docs/en_US/release_notes_4_13.rst index 1f28ec984..f9a747136 100644 --- a/docs/en_US/release_notes_4_13.rst +++ b/docs/en_US/release_notes_4_13.rst @@ -25,6 +25,7 @@ Bug fixes | `Issue #2706 `_ - Added ProjectSet icon for explain module. | `Issue #2828 `_ - Added Gather Merge, Named Tuple Store Scan and Table Function Scan icon for explain module. | `Issue #3778 `_ - Ensure Boolean columns should be editable using keyboard keys. +| `Issue #3936 `_ - Further code refactoring to stabilise the Feature Tests. | `Issue #4381 `_ - Fix an issue where oid column should not be pasted when copy/paste row is used on query output containing the oid column. | `Issue #4419 `_ - Fix a debugger error when using Python 2.7. | `Issue #4486 `_ - Ensure View should be created with special characters. diff --git a/web/pgadmin/feature_tests/browser_tool_bar_test.py b/web/pgadmin/feature_tests/browser_tool_bar_test.py index 2d504d8ae..9e1ff74cb 100644 --- a/web/pgadmin/feature_tests/browser_tool_bar_test.py +++ b/web/pgadmin/feature_tests/browser_tool_bar_test.py @@ -10,11 +10,11 @@ from __future__ import print_function import sys import random + from regression.python_test_utils import test_utils from regression.feature_utils.locators import BrowserToolBarLocators from regression.feature_utils.base_feature_test import BaseFeatureTest -from selenium.common.exceptions import TimeoutException, \ - StaleElementReferenceException +from selenium.webdriver.common.by import By class BrowserToolBarFeatureTest(BaseFeatureTest): @@ -63,18 +63,10 @@ class BrowserToolBarFeatureTest(BaseFeatureTest): self.page.toggle_open_tree_item(self.server['name']) self.page.toggle_open_tree_item('Databases') self.page.toggle_open_tree_item(self.test_db) - retry_count = 0 - while retry_count < 5: - try: - self.page.find_by_css_selector( - BrowserToolBarLocators.open_query_tool_button_css)\ - .click() - break - except (StaleElementReferenceException, TimeoutException): - retry_count += 1 - - self.page.find_by_css_selector( - BrowserToolBarLocators.query_tool_panel_css) + self.page.retry_click( + (By.CSS_SELECTOR, + BrowserToolBarLocators.open_query_tool_button_css), + (By.CSS_SELECTOR, BrowserToolBarLocators.query_tool_panel_css)) def test_view_data_tool_button(self): self.page.select_tree_item(self.test_db) @@ -83,27 +75,14 @@ class BrowserToolBarFeatureTest(BaseFeatureTest): self.page.toggle_open_tables_node() self.page.select_tree_item(self.test_table_name) - retry_count = 0 - while retry_count < 5: - try: - self.page.find_by_css_selector( - BrowserToolBarLocators.view_table_data_button_css).click() - break - except (StaleElementReferenceException, TimeoutException): - retry_count += 1 - self.page.find_by_css_selector( - BrowserToolBarLocators.view_data_panel_css) + self.page.retry_click( + (By.CSS_SELECTOR, + BrowserToolBarLocators.view_table_data_button_css), + (By.CSS_SELECTOR, BrowserToolBarLocators.view_data_panel_css)) def test_filtered_rows_tool_button(self): - retry_count = 0 - while retry_count < 5: - try: - self.page.find_by_css_selector( - BrowserToolBarLocators.filter_data_button_css)\ - .click() - break - except (StaleElementReferenceException, TimeoutException): - retry_count += 1 - self.page.find_by_css_selector( - BrowserToolBarLocators.filter_alertify_box_css) + self.page.retry_click( + (By.CSS_SELECTOR, + BrowserToolBarLocators.filter_data_button_css), + (By.CSS_SELECTOR, BrowserToolBarLocators.filter_alertify_box_css)) self.page.click_modal('Cancel') diff --git a/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py b/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py index 1b97b3e9c..a3a55f32c 100644 --- a/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py +++ b/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py @@ -7,8 +7,10 @@ # ########################################################################## +from __future__ import print_function import pyperclip import random + from selenium.webdriver import ActionChains from selenium.webdriver.common.keys import Keys from regression.python_test_utils import test_utils diff --git a/web/pgadmin/feature_tests/file_manager_test.py b/web/pgadmin/feature_tests/file_manager_test.py index 3df37af84..dd2a7855f 100644 --- a/web/pgadmin/feature_tests/file_manager_test.py +++ b/web/pgadmin/feature_tests/file_manager_test.py @@ -11,12 +11,12 @@ from __future__ import print_function import os import sys import time + from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.by import By from selenium.common.exceptions import StaleElementReferenceException, \ TimeoutException -from selenium.webdriver.support import expected_conditions as EC from regression.feature_utils.base_feature_test import BaseFeatureTest from regression.feature_utils.locators import QueryToolLocators @@ -126,21 +126,11 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): # Intermittently facing issue on first click it is not successful # so tried couple of times. - iteration = 0 - success = False - while not success and iteration < 4: - # Check for sort Ascending - try: - self.page.find_by_xpath("//th[@data-column='0']" - "/div/span[text()='Name']").click() - self.wait.until( - EC.presence_of_element_located(( - By.CSS_SELECTOR, - "#contents th[data-column='0'].tablesorter-headerAsc") - )) - success = True - except Exception as e: - iteration += 1 + success = self.page.retry_click( + (By.XPATH, + "//th[@data-column='0']/div/span[text()='Name']"), + (By.CSS_SELECTOR, + "#contents th[data-column='0'].tablesorter-headerAsc")) if not success: raise Exception("Unable to sort in ascending order while clicked " @@ -151,21 +141,11 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): # Click and Check for sort Descending # Intermittently facing issue on first click it is not successful # so tried couple of times. - iteration = 0 - success = False - while not success and iteration < 4: - - try: - self.page.find_by_xpath("//th[@data-column='0']" - "/div/span[text()='Name']").click() - self.wait.until( - EC.presence_of_element_located(( - By.CSS_SELECTOR, - "#contents th[data-column='0'].tablesorter-headerDesc") - )) - success = True - except Exception as e: - iteration += 1 + success = self.page.retry_click( + (By.XPATH, + "//th[@data-column='0']/div/span[text()='Name']"), + (By.CSS_SELECTOR, + "#contents th[data-column='0'].tablesorter-headerDesc")) if not success: raise Exception("Unable to sort in descending order while clicked " diff --git a/web/pgadmin/feature_tests/pg_datatype_validation_test.py b/web/pgadmin/feature_tests/pg_datatype_validation_test.py index 83a245e57..267fa56a9 100644 --- a/web/pgadmin/feature_tests/pg_datatype_validation_test.py +++ b/web/pgadmin/feature_tests/pg_datatype_validation_test.py @@ -7,6 +7,7 @@ # ########################################################################## +from __future__ import print_function import os import json import time 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 0d4d8e9f6..6b99fdb7d 100644 --- a/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py @@ -6,12 +6,13 @@ # This software is released under the PostgreSQL Licence # ########################################################################## + +from __future__ import print_function import os from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC -from selenium.common.exceptions import ElementClickInterceptedException from regression.feature_utils.base_feature_test import BaseFeatureTest from regression.python_test_utils import test_utils from regression.python_test_utils import test_gui_helper @@ -39,6 +40,11 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.server['name'] ) ) + if '<' in self.database_name and os.name == 'nt': + self.skipTest( + "HTML tags '<' and '>' in object name does not " + "work for windows so skipping the test case" + ) connection = test_utils.get_db_connection( self.server['db'], @@ -63,14 +69,11 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.page.toggle_open_tree_item(self.database_name) # Backup - retry = 3 - while retry > 0: - try: - self.driver.find_element_by_link_text( - NavMenuLocators.tools_menu_link_text).click() - break - except ElementClickInterceptedException: - retry -= 1 + self.page.retry_click( + (By.LINK_TEXT, + NavMenuLocators.tools_menu_link_text), + (By.CSS_SELECTOR, + NavMenuLocators.backup_obj_css)) backup_object = self.wait.until(EC.visibility_of_element_located( (By.CSS_SELECTOR, NavMenuLocators.backup_obj_css))) diff --git a/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py b/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py index 7524f141d..5eb521be7 100644 --- a/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py @@ -6,15 +6,18 @@ # This software is released under the PostgreSQL Licence # ########################################################################## + +from __future__ import print_function +import random +import os + from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By -from selenium.common.exceptions import ElementClickInterceptedException from regression.feature_utils.base_feature_test import BaseFeatureTest from regression.python_test_utils import test_utils from regression.python_test_utils import test_gui_helper from regression.feature_utils.locators import NavMenuLocators -import random class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): @@ -48,6 +51,11 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): self.server['name'] ) ) + if '<' in self.table_name and os.name == 'nt': + self.skipTest( + "HTML tags '<' and '>' in object name does not " + "work for windows so skipping the test case" + ) connection = test_utils.get_db_connection( self.server['db'], @@ -90,15 +98,12 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): self.page.toggle_open_tree_item('public') self.page.toggle_open_tables_node() self.page.select_tree_item(self.table_name) - retry = 3 - while retry > 0: - try: - tools_menu = self.driver.find_element_by_link_text( - NavMenuLocators.tools_menu_link_text) - tools_menu.click() - break - except ElementClickInterceptedException: - retry -= 1 + + self.page.retry_click( + (By.LINK_TEXT, + NavMenuLocators.tools_menu_link_text), + (By.CSS_SELECTOR, NavMenuLocators.maintenance_obj_css)) + maintenance_obj = self.wait.until(EC.visibility_of_element_located( (By.CSS_SELECTOR, NavMenuLocators.maintenance_obj_css))) maintenance_obj.click() diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index fbd0d28e3..937cd97a8 100644 --- a/web/pgadmin/feature_tests/query_tool_journey_test.py +++ b/web/pgadmin/feature_tests/query_tool_journey_test.py @@ -308,9 +308,7 @@ class QueryToolJourneyTest(BaseFeatureTest): query_options = self.page.find_by_css_selector( QueryToolLocators.btn_query_dropdown) query_options.click() - self.page.find_by_css_selector( - QueryToolLocators.btn_auto_commit).click() - query_options.click() # Click again to close dropdown + self.page.uncheck_execute_option("auto_commit") self._update_numeric_cell(2, 10) @@ -320,9 +318,7 @@ class QueryToolJourneyTest(BaseFeatureTest): query_options = self.page.find_by_css_selector( QueryToolLocators.btn_query_dropdown) query_options.click() - self.page.find_by_css_selector( - QueryToolLocators.btn_auto_commit).click() - query_options.click() # Click again to close dropdown + self.page.check_execute_option("auto_commit") def _check_history_queries_and_icons(self, history_queries, history_icons): # Select first query history entry @@ -422,9 +418,6 @@ class QueryToolJourneyTest(BaseFeatureTest): def _check_cell_editable(self, cell_index): """Checks if a cell in the first row of the resultset is editable""" - # self.page.check_if_element_exist_by_xpath( - # "//div[contains(@style, 'top:0px')]//div[contains(@class, " - # "'l{0} r{1}')]".format(cell_index, cell_index)) cell_el = self.page.find_by_xpath( "//div[contains(@style, 'top:0px')]//div[contains(@class, " "'l{0} r{1}')]".format(cell_index, cell_index)) diff --git a/web/pgadmin/feature_tests/query_tool_tests.py b/web/pgadmin/feature_tests/query_tool_tests.py index 9252b93ca..ca9ee5091 100644 --- a/web/pgadmin/feature_tests/query_tool_tests.py +++ b/web/pgadmin/feature_tests/query_tool_tests.py @@ -9,6 +9,7 @@ from __future__ import print_function import sys +import time from selenium.common.exceptions import StaleElementReferenceException, \ ElementClickInterceptedException @@ -242,7 +243,6 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( def _check_ondemand_result(self, row_id_to_find): # scroll to bottom to bring last row of next chunk in viewport. - # canvas_ele = self.page.find_by_css_selector() scroll = 10 while scroll: canvas_ele = self.page.find_by_css_selector('.grid-canvas') @@ -252,8 +252,9 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( ".scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas')" ".height());" ) - import time - time.sleep(0.5) + # Table height takes some time to update, for which their is no + # particular way + time.sleep(1) if canvas_ele.size['height'] == scrolling_height: break else: diff --git a/web/pgadmin/feature_tests/table_ddl_feature_test.py b/web/pgadmin/feature_tests/table_ddl_feature_test.py index bd9d45fa1..12a293728 100644 --- a/web/pgadmin/feature_tests/table_ddl_feature_test.py +++ b/web/pgadmin/feature_tests/table_ddl_feature_test.py @@ -7,6 +7,7 @@ # ########################################################################## +from __future__ import print_function import random from regression.feature_utils.base_feature_test import BaseFeatureTest diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/pgadmin/feature_tests/view_data_dml_queries.py index a9e9f48ec..09dd6d046 100644 --- a/web/pgadmin/feature_tests/view_data_dml_queries.py +++ b/web/pgadmin/feature_tests/view_data_dml_queries.py @@ -7,6 +7,7 @@ # ########################################################################## +from __future__ import print_function import json import os import time diff --git a/web/pgadmin/feature_tests/xss_checks_panels_and_query_tool_test.py b/web/pgadmin/feature_tests/xss_checks_panels_and_query_tool_test.py index 9b2f31d39..335d2c8db 100644 --- a/web/pgadmin/feature_tests/xss_checks_panels_and_query_tool_test.py +++ b/web/pgadmin/feature_tests/xss_checks_panels_and_query_tool_test.py @@ -10,6 +10,7 @@ from __future__ import print_function import sys import random + from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest from selenium.webdriver import ActionChains 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 be275913f..70ae11d13 100644 --- a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py +++ b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py @@ -7,6 +7,7 @@ # ########################################################################## +from __future__ import print_function import random from selenium.webdriver import ActionChains 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 6673390a2..bf0013597 100644 --- a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py +++ b/web/pgadmin/feature_tests/xss_checks_roles_control_test.py @@ -6,6 +6,8 @@ # This software is released under the PostgreSQL Licence # ########################################################################## + +from __future__ import print_function import random from regression.python_test_utils import test_utils @@ -89,16 +91,8 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): 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 + + self.page.retry_click( + (By.XPATH, + "//a[normalize-space(text())='Membership']"), + (By.XPATH, "//input[@placeholder='Select members']")) diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index b5b8fb5d7..3b18057f0 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -175,6 +175,38 @@ class PgadminPage: execute_button.click() self.wait_for_query_tool_loading_indicator_to_disappear() + def check_execute_option(self, option): + """"This function will check auto commit or auto roll back based on + user input. If button is already checked, no action will be taken""" + if option == 'auto_commit': + check_status = self.driver.find_element_by_css_selector( + QueryToolLocators.btn_auto_commit_check_status) + if 'visibility-hidden' in check_status.get_attribute('class'): + self.find_by_css_selector( + QueryToolLocators.btn_auto_commit).click() + if option == 'auto_rollback': + check_status = self.driver.find_element_by_css_selector( + QueryToolLocators.btn_auto_rollback_check_status) + if 'visibility-hidden' in check_status.get_attribute('class'): + self.find_by_css_selector( + QueryToolLocators.btn_auto_rollback).click() + + def uncheck_execute_option(self, option): + """"This function will uncheck auto commit or auto roll back based on + user input. If button is already unchecked, no action will be taken""" + if option == 'auto_commit': + check_status = self.driver.find_element_by_css_selector( + QueryToolLocators.btn_auto_commit_check_status) + if 'visibility-hidden' not in check_status.get_attribute('class'): + self.find_by_css_selector( + QueryToolLocators.btn_auto_commit).click() + if option == 'auto_rollback': + check_status = self.driver.find_element_by_css_selector( + QueryToolLocators.btn_auto_rollback_check_status) + if 'visibility-hidden' not in check_status.get_attribute('class'): + self.find_by_css_selector( + QueryToolLocators.btn_auto_rollback).click() + def close_data_grid(self): self.driver.switch_to_default_content() xpath = "//*[@id='dockerContainer']/div/div[3]/div/div[2]/div[1]" @@ -712,24 +744,17 @@ class PgadminPage: status_changed_successfully = True return status_changed_successfully - def retry_click_operation(self, element_to_click, - element_to_verify_after_click): - """This will attempt to click add button multiple time, - some different exception encountered while clicking, so handled - through this""" - + def retry_click(self, click_locator, verify_locator): click_status = False attempt = 0 while click_status is not True and attempt < 5: try: - if element_to_verify_after_click.is_displayed(): - click_status = True - element_to_click.click() - if element_to_verify_after_click.is_displayed(): - click_status = True + element = self.driver.find_element(*click_locator) + element.click() + WebDriverWait(self.driver, 5).until( + EC.visibility_of_element_located(verify_locator)) + click_status = True except Exception: - print("The click operation is not performed for " - "attempt %s, will try 5 attempts" % attempt) - attempt = +1 + attempt = +1 return click_status