diff --git a/web/pgadmin/browser/static/js/frame.js b/web/pgadmin/browser/static/js/frame.js index a062a6a2..5e5e7df1 100644 --- a/web/pgadmin/browser/static/js/frame.js +++ b/web/pgadmin/browser/static/js/frame.js @@ -53,7 +53,7 @@ define([ myPanel.closeable(!!that.isCloseable); - var $frameArea = $('
'); + var $frameArea = $('
'); myPanel.layout().addItem($frameArea); that.panel = myPanel; var frame = new wcIFrame($frameArea, myPanel); 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 382020f6..0837b84a 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 @@ -43,9 +43,6 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): self.page.toggle_open_tree_item(self.test_db) self.page.open_query_tool() - self.page.driver.switch_to_frame( - self.page.driver.find_element_by_tag_name("iframe")) - self.page.fill_codemirror_area_with( "SELECT * FROM %s ORDER BY some_column" % self.test_table_name) diff --git a/web/pgadmin/feature_tests/file_manager_test.py b/web/pgadmin/feature_tests/file_manager_test.py index 90548079..288dc983 100644 --- a/web/pgadmin/feature_tests/file_manager_test.py +++ b/web/pgadmin/feature_tests/file_manager_test.py @@ -17,6 +17,7 @@ from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from regression.feature_utils.base_feature_test import BaseFeatureTest +from .locators import QueryToolLocatorsCss class CheckFileManagerFeatureTest(BaseFeatureTest): @@ -41,7 +42,7 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): os.remove(self.XSS_FILE) def after(self): - self.page.close_query_tool('.sql', False) + self.page.close_query_tool(False) self.page.remove_server(self.server) def runTest(self): @@ -64,14 +65,11 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): self.page.open_query_tool() def _create_new_file(self): - self.page.find_by_id("btn-save").click() - self.page.wait_for_query_tool_loading_indicator_to_disappear() - self.page.find_by_css_selector('.change_file_types') + self.page.find_by_css_selector(QueryToolLocatorsCss.btn_save).click() # Set the XSS value in input - self.page.find_by_id("file-input-path").clear() - self.page.find_by_id("file-input-path").send_keys( - self.XSS_FILE - ) + 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) # Save the file self.page.click_modal('Create') self.page.wait_for_query_tool_loading_indicator_to_disappear() @@ -79,13 +77,8 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): def _open_file_manager_and_check_xss_file(self): self.page.find_by_id("btn-load-file").click() self.page.find_by_css_selector('.change_file_types') - self.page.find_by_id("file-input-path").clear() - self.page.find_by_id("file-input-path").send_keys( - '/tmp/' - ) - self.page.find_by_id("file-input-path").send_keys( - Keys.RETURN - ) + self.page.fill_input_by_css_selector("#file-input-path", "/tmp/", + key_after_input=Keys.RETURN) if self.page.driver.capabilities['browserName'] == 'firefox': table = self.page.wait_for_element_to_reload( diff --git a/web/pgadmin/feature_tests/locators.py b/web/pgadmin/feature_tests/locators.py new file mode 100644 index 00000000..34aea87c --- /dev/null +++ b/web/pgadmin/feature_tests/locators.py @@ -0,0 +1,21 @@ +class QueryToolLocatorsCss: + btn_save = "#btn-save" + btn_execute_query = "#btn-flash" + btn_query_dropdown = "#btn-query-dropdown" + btn_auto_rollback = "#btn-auto-rollback" + btn_auto_commit = "#btn-auto-commit" + btn_cancel_query = "#btn-cancel-query" + btn_explain = "#btn-explain" + btn_explain_analyze = "#btn-explain-analyze" + btn_explain_options_dropdown = "#btn-explain-options-dropdown" + btn_explain_verbose = "#btn-explain-verbose" + btn_explain_costs = "#btn-explain-costs" + btn_explain_buffers = "#btn-explain-buffers" + btn_explain_timing = "#btn-explain-timing" + btn_clear_dropdown = "#btn-clear-dropdown" + btn_clear = "#btn-clear" + + query_editor_panel = "#output-panel" + query_history_selected = "#query_list .selected" + query_history_detail = "#query_detail" + editor_panel = "#output-panel" 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 1c89393d..c98e2fba 100644 --- a/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py @@ -69,7 +69,8 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, ".file [name='file']"))).click() - self.page.fill_input_by_field_name("file", "test_backup") + self.page.fill_input_by_field_name( + "file", "test_backup", loose_focus=True) self.page.find_by_xpath("//button[contains(@class,'fa-save') " "and contains(.,'Backup')]").click() @@ -83,6 +84,7 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.page.find_by_css_selector( ".pg-bg-more-details").click() + backup_file = None # Check for XSS in Backup details if self.is_xss_check: self._check_detailed_window_for_xss('Backup') @@ -99,7 +101,6 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.assertIn("pg_dump", str(command)) - backup_file = None if command: backup_file = command[int(command.find('--file')) + 8:int(command.find('--host')) - 2] @@ -117,7 +118,8 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, ".file [name='file']"))).click() - self.page.fill_input_by_field_name("file", "test_backup") + self.page.fill_input_by_field_name( + "file", "test_backup", loose_focus=True) self.page.find_by_xpath("//button[contains(@class,'fa-upload')" " and contains(.,'Restore')]").click() diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index d863f8e8..670de66f 100644 --- a/web/pgadmin/feature_tests/query_tool_journey_test.py +++ b/web/pgadmin/feature_tests/query_tool_journey_test.py @@ -15,6 +15,7 @@ from selenium.webdriver.common.keys import Keys from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest +from .locators import QueryToolLocatorsCss class QueryToolJourneyTest(BaseFeatureTest): @@ -73,16 +74,18 @@ class QueryToolJourneyTest(BaseFeatureTest): def _test_history_tab(self): self.__clear_query_tool() - editor_input = self.page.find_by_id("output-panel") + editor_input = self.page.find_by_css_selector( + QueryToolLocatorsCss.query_editor_panel) self.page.click_element(editor_input) self._execute_query("SELECT * FROM table_that_doesnt_exist") self.page.click_tab("Query History") selected_history_entry = self.page.find_by_css_selector( - "#query_list .selected") + QueryToolLocatorsCss.query_history_selected) self.assertIn("SELECT * FROM table_that_doesnt_exist", selected_history_entry.text) - failed_history_detail_pane = self.page.find_by_id("query_detail") + failed_history_detail_pane = self.page.find_by_css_selector( + QueryToolLocatorsCss.query_history_detail) self.assertIn( "Error Message relation \"table_that_doesnt_exist\" " @@ -108,7 +111,8 @@ class QueryToolJourneyTest(BaseFeatureTest): newly_selected_history_entry = self.page.find_by_xpath( "//*[@id='query_list']/ul/li[2]") self.page.click_element(newly_selected_history_entry) - selected_history_detail_pane = self.page.find_by_id("query_detail") + selected_history_detail_pane = self.page.find_by_css_selector( + QueryToolLocatorsCss.query_history_detail) self.assertIn("SELECT * FROM table_that_doesnt_exist", selected_history_detail_pane.get_attribute('innerHTML')) @@ -118,7 +122,8 @@ class QueryToolJourneyTest(BaseFeatureTest): self.page.fill_codemirror_area_with("SELECT * FROM hats") for _ in range(15): - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab("Query History") @@ -140,7 +145,8 @@ class QueryToolJourneyTest(BaseFeatureTest): self.page.click_element(editor_input) self.page.fill_codemirror_area_with("SELECT * FROM hats") for _ in range(15): - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab("History") @@ -173,7 +179,8 @@ class QueryToolJourneyTest(BaseFeatureTest): def _execute_query(self, query): self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() def _assert_clickable(self, element): self.page.click_element(element) diff --git a/web/pgadmin/feature_tests/query_tool_tests.py b/web/pgadmin/feature_tests/query_tool_tests.py index c6e609cc..3372ccf9 100644 --- a/web/pgadmin/feature_tests/query_tool_tests.py +++ b/web/pgadmin/feature_tests/query_tool_tests.py @@ -18,6 +18,7 @@ from selenium.webdriver.common.by import By from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest import config +from .locators import QueryToolLocatorsCss class QueryToolFeatureTest(BaseFeatureTest): @@ -110,28 +111,34 @@ class QueryToolFeatureTest(BaseFeatureTest): # this will set focus to correct iframe. self.page.fill_codemirror_area_with('') - explain_op = self.page.find_by_id("btn-explain-options-dropdown") + explain_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain_options_dropdown) explain_op.click() # disable Explain options and auto rollback only if they are enabled. - for op in ('explain-verbose', 'explain-costs', - 'explain-buffers', 'explain-timing'): - btn = self.page.find_by_id("btn-{}".format(op)) + for op in (QueryToolLocatorsCss.btn_explain_verbose, + QueryToolLocatorsCss.btn_explain_costs, + QueryToolLocatorsCss.btn_explain_buffers, + QueryToolLocatorsCss.btn_explain_timing): + btn = self.page.find_by_css_selector(op) check = btn.find_element_by_tag_name('i') if 'visibility-hidden' not in check.get_attribute('class'): btn.click() - query_op = self.page.find_by_id("btn-query-dropdown") + query_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_query_dropdown) query_op.click() # disable auto rollback only if they are enabled - btn = self.page.find_by_id("btn-auto-rollback") + btn = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_rollback) check = btn.find_element_by_tag_name('i') if 'visibility-hidden' not in check.get_attribute('class'): btn.click() # enable autocommit only if it's disabled - btn = self.page.find_by_id("btn-auto-commit") + btn = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_commit) check = btn.find_element_by_tag_name('i') if 'visibility-hidden' in check.get_attribute('class'): btn.click() @@ -145,14 +152,14 @@ class QueryToolFeatureTest(BaseFeatureTest): self.page.toggle_open_tree_item(self.test_db) def _clear_query_tool(self): - self.page.click_element( - self.page.find_by_xpath("//*[@id='btn-clear-dropdown']") + self.page.click_element(self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_clear_dropdown) ) ActionChains(self.driver) \ - .move_to_element(self.page.find_by_xpath("//*[@id='btn-clear']")) \ - .perform() + .move_to_element(self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_clear)).perform() self.page.click_element( - self.page.find_by_xpath("//*[@id='btn-clear']") + self.page.find_by_css_selector(QueryToolLocatorsCss.btn_clear) ) self.page.click_modal('Yes') @@ -171,7 +178,8 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( wait = WebDriverWait(self.page.driver, 10) self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() wait.until(EC.presence_of_element_located( (By.XPATH, @@ -192,7 +200,8 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( print("On demand result set on grid select all... ", file=sys.stderr, end="") - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() wait.until(EC.presence_of_element_located( (By.XPATH, @@ -210,7 +219,8 @@ SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( print("On demand result set on column select all... ", file=sys.stderr, end="") - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() @@ -252,16 +262,19 @@ SELECT generate_series(1, 1000) as id order by id desc""" self.page.fill_codemirror_area_with(query) - explain_op = self.page.find_by_id("btn-explain-options-dropdown") + explain_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain_options_dropdown) explain_op.click() # disable Explain options and auto rollback only if they are enabled. - for op in ('explain-verbose', 'explain-costs'): - self.page.find_by_id("btn-{}".format(op)).click() + for op in (QueryToolLocatorsCss.btn_explain_verbose, + QueryToolLocatorsCss.btn_explain_costs): + self.page.find_by_css_selector(op).click() explain_op.click() - self.page.find_by_id("btn-explain").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() @@ -285,16 +298,19 @@ SELECT generate_series(1, 1000) as id order by id desc""" self.page.fill_codemirror_area_with(query) - explain_op = self.page.find_by_id("btn-explain-options-dropdown") + explain_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain_options_dropdown) explain_op.click() # disable Explain options and auto rollback only if they are enabled. - for op in ('explain-buffers', 'explain-timing'): - self.page.find_by_id("btn-{}".format(op)).click() + for op in (QueryToolLocatorsCss.btn_explain_buffers, + QueryToolLocatorsCss.btn_explain_timing): + self.page.find_by_css_selector(op).click() explain_op.click() - self.page.find_by_id("btn-explain-analyze").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain_analyze).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() @@ -324,14 +340,17 @@ CREATE TABLE public.{}();""".format(table_name) self.page.fill_codemirror_area_with(query) - query_op = self.page.find_by_id("btn-query-dropdown") + query_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_query_dropdown) query_op.click() - self.page.find_by_id("btn-auto-commit").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_commit).click() query_op.click() - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -347,7 +366,8 @@ CREATE TABLE public.{}();""".format(table_name) -- 4. Check if table is *NOT* created. ROLLBACK;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -364,7 +384,8 @@ ROLLBACK;""" SELECT relname FROM pg_class WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Data Output') @@ -393,14 +414,17 @@ END;""" wait = WebDriverWait(self.page.driver, 10) - query_op = self.page.find_by_id("btn-query-dropdown") + query_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_query_dropdown) query_op.click() - self.page.find_by_id("btn-auto-commit").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_commit).click() query_op.click() - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() @@ -416,7 +440,8 @@ CREATE TABLE public.{}();""".format(table_name) self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -433,7 +458,8 @@ CREATE TABLE public.{}();""".format(table_name) -- 5. Check if table is created event after ROLLBACK. ROLLBACK;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -451,7 +477,8 @@ ROLLBACK;""" SELECT relname FROM pg_class WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.click_tab('Data Output') self.page.wait_for_query_tool_loading_indicator_to_disappear() @@ -479,16 +506,20 @@ END;""" self.page.fill_codemirror_area_with(query) - query_op = self.page.find_by_id("btn-query-dropdown") + query_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_query_dropdown) query_op.click() - self.page.find_by_id("btn-auto-rollback").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_rollback).click() - self.page.find_by_id("btn-auto-commit").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_commit).click() query_op.click() - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self._clear_query_tool() @@ -504,7 +535,8 @@ CREATE TABLE public.{}();""".format(table_name) self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -522,7 +554,8 @@ CREATE TABLE public.{}();""".format(table_name) -- 6. Check if table is *NOT* created after ending transaction. SELECT 1/0;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -541,7 +574,8 @@ SELECT 1/0;""" END;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -560,7 +594,8 @@ END;""" SELECT relname FROM pg_class WHERE relkind IN ('r','s','t') and relnamespace = 2200::oid;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Data Output') @@ -584,10 +619,12 @@ SELECT 1, pg_sleep(300)""" self.page.fill_codemirror_area_with(query) - query_op = self.page.find_by_id("btn-query-dropdown") + query_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_query_dropdown) query_op.click() - auto_rollback_btn = self.page.find_by_id("btn-auto-rollback") + auto_rollback_btn = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_commit) auto_rollback_check = auto_rollback_btn.find_element_by_tag_name("i") @@ -600,7 +637,8 @@ SELECT 1, pg_sleep(300)""" auto_rollback_check.get_attribute('class')): auto_rollback_btn.click() - auto_commit_btn = self.page.find_by_id("btn-auto-commit") + auto_commit_btn = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_auto_commit) auto_commit_check = auto_commit_btn.find_element_by_tag_name("i") @@ -615,10 +653,12 @@ SELECT 1, pg_sleep(300)""" query_op.click() - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.find_by_xpath("//*[@id='fetching_data']") - self.page.find_by_id("btn-cancel-query").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_cancel_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') self.page.find_by_xpath( @@ -643,7 +683,8 @@ SELECT 1, pg_sleep(300)""" print("\n\tListen on an event... ", file=sys.stderr, end="") self.page.fill_codemirror_area_with("LISTEN foo;") - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') @@ -656,7 +697,8 @@ SELECT 1, pg_sleep(300)""" print("\tNotify event without data... ", file=sys.stderr, end="") self.page.fill_codemirror_area_with("NOTIFY foo;") - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Notifications') @@ -670,7 +712,8 @@ SELECT 1, pg_sleep(300)""" if self._supported_server_version(): self.page.fill_codemirror_area_with("SELECT pg_notify('foo', " "'Hello')") - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Notifications') @@ -707,22 +750,26 @@ SELECT 1, pg_sleep(300)""" wait = WebDriverWait(self.page.driver, 10) self.page.fill_codemirror_area_with("SET jit_above_cost=10;") - self.page.find_by_id("btn-flash").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self._clear_query_tool() self.page.fill_codemirror_area_with("SELECT count(*) FROM pg_class;") - explain_op = self.page.find_by_id("btn-explain-options-dropdown") + explain_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain_options_dropdown) explain_op.click() # disable Explain options and auto rollback only if they are enabled. - for op in ('explain-verbose', 'explain-costs', 'explain-analyze'): - self.page.find_by_id("btn-{}".format(op)).click() + for op in (QueryToolLocatorsCss.btn_explain_verbose, + QueryToolLocatorsCss.btn_explain_costs): + self.page.find_by_css_selector(op).click() explain_op.click() - self.page.find_by_id("btn-explain-analyze").click() + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain_analyze).click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Data Output') diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/pgadmin/feature_tests/view_data_dml_queries.py index 1be9dcfa..3c00d571 100644 --- a/web/pgadmin/feature_tests/view_data_dml_queries.py +++ b/web/pgadmin/feature_tests/view_data_dml_queries.py @@ -109,7 +109,7 @@ CREATE TABLE public.defaults_{0} for cnt in (1, 2): self.page.select_tree_item('defaults_{0}'.format(str(cnt))) # Open Object -> View/Edit data - self._view_data_grid() + self._view_data_grid('defaults_{0}'.format(str(cnt))) self.page.wait_for_query_tool_loading_indicator_to_disappear() # Run test to insert a new row in table with default values @@ -214,7 +214,7 @@ CREATE TABLE public.defaults_{0} self.page.toggle_open_tree_item('public') self.page.toggle_open_tree_item('Tables') - def _view_data_grid(self): + def _view_data_grid(self, table_name): self.page.driver.find_element_by_link_text("Object").click() ActionChains( self.page.driver @@ -224,7 +224,8 @@ CREATE TABLE public.defaults_{0} self.page.find_by_partial_link_text("All Rows").click() time.sleep(1) # wait until datagrid frame is loaded. - self.page.click_tab('Edit Data -') + + self.page.click_tab(table_name) self.wait.until( EC.visibility_of_element_located( @@ -309,6 +310,7 @@ CREATE TABLE public.defaults_{0} if (idx != 1 and not is_new_row) or is_new_row: self.assertEquals(element.text, config_data[str(idx)][1]) + self.assertEquals(element.text, config_data[str(idx)][1]) # scroll browser back to the left # to reset position so other assertions can succeed 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 27ac8c2c..8175bf9f 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 @@ -52,13 +52,21 @@ class CheckForXssFeatureTest(BaseFeatureTest): self._check_xss_in_browser_tree() self._check_xss_in_properties_tab() self._check_xss_in_sql_tab() + + # sometime the tab for dependent does not show info, so refreshing + # the page and then again collapsing until the table node + self.page.refresh_page() + self.page.toggle_open_servers_group() + self._tables_node_expandable() self._check_xss_in_dependents_tab() # Query tool + self.page.open_query_tool() self._check_xss_in_query_tool() self.page.close_query_tool() # Explain module + self.page.open_query_tool() self._check_xss_in_explain_module() self.page.close_query_tool() @@ -125,6 +133,7 @@ class CheckForXssFeatureTest(BaseFeatureTest): # Create any constraint with xss name to test this def _check_xss_in_dependents_tab(self): + print( "\n\tChecking the Dependents tab for the XSS", file=sys.stderr, end="" @@ -142,17 +151,11 @@ class CheckForXssFeatureTest(BaseFeatureTest): "Dependents tab (BackGrid)" ) - def _open_query_tool(self): - self.page.driver.find_element_by_link_text("Tools").click() - self.page.find_by_partial_link_text("Query Tool").click() - self.page.click_tab('Query -') - def _check_xss_in_query_tool(self): print( "\n\tChecking the SlickGrid cell for the XSS", file=sys.stderr, end="" ) - self._open_query_tool() self.page.fill_codemirror_area_with( "select ''" ) @@ -179,14 +182,10 @@ class CheckForXssFeatureTest(BaseFeatureTest): "\n\tChecking the Graphical Explain plan for the XSS ...", file=sys.stderr, end="" ) - self._open_query_tool() self.page.fill_codemirror_area_with( 'select * from "{0}"'.format(self.test_table_name) ) - query_op = self.page.find_by_id("btn-query-dropdown") - query_op.click() - self.page.find_by_id("btn-explain").click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Explain') diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js index 0a795fab..4ed072b6 100644 --- a/web/pgadmin/tools/datagrid/static/js/datagrid.js +++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js @@ -54,14 +54,7 @@ define('pgadmin.datagrid', [ self.preferences = pgBrowser.get_preferences_for_module('sqleditor'); }); - this.spinner_el = - `
-
-
-
-
-
-
`; + // Define list of nodes on which view data option appears var supported_nodes = [ 'table', 'view', 'mview', @@ -525,17 +518,26 @@ define('pgadmin.datagrid', [ var openQueryToolURL = function(j) { // add spinner element - $(j).data('embeddedFrame').$container.append(pgAdmin.DataGrid.spinner_el); - setTimeout(function() { + let $spinner_el = + $(`
+
+
+
+
+
+
`).appendTo($(j).data('embeddedFrame').$container); + + let init_poller_id = setInterval(function() { var frameInitialized = $(j).data('frameInitialized'); if (frameInitialized) { + clearInterval(init_poller_id); var frame = $(j).data('embeddedFrame'); if (frame) { + frame.onLoaded(()=>{ + $spinner_el.remove(); + }); frame.openURL(baseUrl); - frame.$container.find('.pg-sp-container').delay(1000).hide(1); } - } else { - openQueryToolURL(j); } }, 100); }; diff --git a/web/pgadmin/utils/route.py b/web/pgadmin/utils/route.py index e67f0195..b206cb2e 100644 --- a/web/pgadmin/utils/route.py +++ b/web/pgadmin/utils/route.py @@ -53,7 +53,7 @@ class TestsGeneratorRegistry(ABCMeta): ABCMeta.__init__(cls, name, bases, d) @classmethod - def load_generators(cls, pkg_root, exclude_pkgs): + def load_generators(cls, pkg_root, exclude_pkgs, for_modules=[]): cls.registry = dict() @@ -61,6 +61,13 @@ class TestsGeneratorRegistry(ABCMeta): all_modules += find_modules(pkg_root, False, True) + # If specific modules are to be tested, exclude others + if len(for_modules) > 0: + all_modules = [module_name + for module_name in all_modules + for fmod in for_modules + if module_name.endswith(fmod)] + # Check for SERVER mode for module_name in all_modules: try: diff --git a/web/regression/README b/web/regression/README index 7e668cfd..6a68d36f 100644 --- a/web/regression/README +++ b/web/regression/README @@ -167,6 +167,14 @@ Python Tests: Example 2) Run test framework for 'database' node run 'python runtests.py --pkg browser.server_groups.servers.databases.tests' +- Execute test framework for certain modules of a test pkg + + Example 1) Run test framework for 'sqleditor' package and test_start_running_query module + run 'python runtests.py --pkg tools.sqleditor --modules test_start_running_query' + + Example 2) Run test framework for 'sqleditor' package and test_start_running_query,test_query_tool_fs_utils modules + run 'python runtests.py --pkg tools.sqleditor --modules test_start_running_query,test_query_tool_fs_utils' + - Exclude a package and its subpackages when running tests: Example: exclude feature tests but run all others: diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index 0c8f0fa3..72fbd356 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -10,7 +10,8 @@ import time from selenium.common.exceptions import NoSuchElementException, \ - WebDriverException, TimeoutException, NoSuchWindowException + WebDriverException, TimeoutException, NoSuchWindowException, \ + StaleElementReferenceException from selenium.webdriver import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as EC @@ -93,7 +94,6 @@ class PgadminPage: self.enable_menu_item(query_tool, 10) self.find_by_partial_link_text("Query Tool").click() - self.click_tab('Query -') def enable_menu_item(self, menu_item, wait_time): start_time = time.time() @@ -110,11 +110,10 @@ class PgadminPage: else: assert False, "'Tools -> Query Tool' menu did not enable." - def close_query_tool(self, name="Query", prompt=True): + def close_query_tool(self, prompt=True): self.driver.switch_to.default_content() tab = self.find_by_xpath( - "//*[contains(@class,'wcPanelTab') and " - "contains(.,'" + name + "')]") + "//div[@class='wcPanelTab wcPanelTabActive']") ActionChains(self.driver).context_click(tab).perform() self.find_by_xpath( "//li[contains(@class, 'context-menu-item')]/span[contains(text()," @@ -149,18 +148,37 @@ class PgadminPage: def select_tree_item(self, tree_item_text): item = self.find_by_xpath( - "//*[@id='tree']//*[.='" + tree_item_text + - "' and @class='aciTreeItem']") + "//*[@id='tree']//*[contains(text(), '" + tree_item_text + "')]" + "/parent::span[@class='aciTreeItem']") self.driver.execute_script("arguments[0].scrollIntoView()", item) item.click() - def toggle_open_tree_item(self, tree_item_text): - item = self.find_by_xpath( - "//*[@id='tree']//*[.='" + tree_item_text + - "']/../*[@class='aciTreeButton']") + def toggle_open_servers_group(self): + """This will open Servers group to display underlying nodes""" + self.wait_for_spinner_to_disappear() + server_group = self.find_by_xpath( + "//div[@id='tree']//span[@class='aciTreeItem']" + "/span[(@class='aciTreeText') and starts-with(text(),'Servers ') " + "or starts-with(text(), 'Servers')]") + ActionChains(self.driver).double_click(server_group).perform() - self.driver.execute_script("arguments[0].scrollIntoView()", item) - item.click() + def toggle_open_tree_item(self, tree_item_text): + # 'sleep' here helps in cases where underlying nodes are auto opened. + # Otherwise, encountered situations where False value is returned + # even if the underlying node to be clicked was Opened. + time.sleep(.3) + item_with_text = self.find_by_xpath( + "//div[@id='tree']//span[@class='aciTreeItem']/span[" + "(@class='aciTreeText') and text()='" + tree_item_text + "']") + + self.driver.execute_script("arguments[0].scrollIntoView()", + item_with_text) + if item_with_text.find_element_by_xpath( + ".//ancestor::*[@class='aciTreeLine']").get_attribute( + "aria-expanded") == 'false': + item = item_with_text.find_element_by_xpath( + ".//parent::*[@class='aciTreeItem']") + ActionChains(self.driver).double_click(item).perform() def toggle_open_server(self, tree_item_text): def check_for_password_dialog_or_tree_open(driver): @@ -224,38 +242,103 @@ class PgadminPage: "clicking the element not to throw an exception", click_succeeded ) - def fill_input_by_field_name(self, field_name, field_content): - field = self.find_by_xpath("//input[@name='" + field_name + "']") - backspaces = [Keys.BACKSPACE] * len(field.get_attribute('value')) + def js_send_key(self, field, sele_key): + keycode = None + + if sele_key in (Keys.RETURN, Keys.ENTER): + keycode = 13 + elif sele_key == Keys.ARROW_DOWN: + keycode = 40 + self.driver.execute_script( + "arguments[0].dispatchEvent(new KeyboardEvent('keydown', " + "{'keyCode':arguments[1], 'which':arguments[1]}));" + "arguments[0].dispatchEvent(new KeyboardEvent('keypress', " + "{'keyCode':arguments[1], 'which':arguments[1]}));" + "arguments[0].dispatchEvent(new KeyboardEvent('keyup', " + "{'keyCode':arguments[1], 'which':arguments[1]}));" + "arguments[0].dispatchEvent(new Event('input'));" + "arguments[0].dispatchEvent(new Event('change'));", + field, keycode) + + def js_loose_focus(self, field): + self.driver.execute_script( + "arguments[0].dispatchEvent(new Event('blur'));", field) + + def fill_input(self, field, field_content, input_keys=False, + key_after_input=Keys.ARROW_DOWN): field.click() - field.send_keys(backspaces) - field.send_keys(str(field_content)) - self.wait_for_input_field_content(field_name, field_content) - def fill_codemirror_area_with(self, field_content): + # Use send keys if input_keys true, else use javascript to set content + if input_keys: + backspaces = [Keys.BACKSPACE] * len(field.get_attribute('value')) + field.send_keys(backspaces) + field.send_keys(str(field_content)) + self.wait_for_input_by_element(field, field_content) + else: + self.driver.execute_script("arguments[0].value = arguments[1]", + field, field_content) + # keycode 40 is for arrow down + self.js_send_key(field, Keys.ARROW_DOWN) + + if key_after_input: + self.js_send_key(field, key_after_input) + + def fill_input_by_field_name(self, field_name, field_content, + input_keys=False, + key_after_input=Keys.ARROW_DOWN, + loose_focus=False): + field = self.find_by_css_selector( + "input[name='" + field_name + "']:not(:disabled)") + self.fill_input(field, field_content, input_keys=input_keys, + key_after_input=key_after_input) + + if loose_focus: + self.js_loose_focus(field) + + def fill_input_by_css_selector(self, css_selector, field_content, + input_keys=False, + key_after_input=Keys.ARROW_DOWN, + loose_focus=False): + field = self.find_by_css_selector(css_selector) + self.fill_input(field, field_content, input_keys=input_keys, + key_after_input=key_after_input) + + if loose_focus: + self.js_loose_focus(field) + + def fill_codemirror_area_with(self, field_content, input_keys=False): def find_codemirror(driver): try: driver.switch_to.default_content() driver.switch_to_frame( driver.find_element_by_tag_name("iframe")) - element = driver.find_element_by_xpath( - "//pre[contains(@class,'CodeMirror-line')]/../../../" - "*[contains(@class,'CodeMirror-code')]") + element = driver.find_element_by_css_selector( + "#output-panel .CodeMirror") if element.is_displayed() and element.is_enabled(): return element except (NoSuchElementException, WebDriverException): return False + codemirror_ele = WebDriverWait( + self.driver, timeout=self.timeout, poll_frequency=0.01)\ + .until(find_codemirror, + "Timed out waiting for codemirror to appear") + time.sleep(1) - WebDriverWait(self.driver, timeout=self.timeout, poll_frequency=0.01).\ - until(find_codemirror, "Timed out waiting for codemirror " - "to appear").click() - time.sleep(1) + codemirror_ele.click() - action = ActionChains(self.driver) - action.send_keys(field_content) - action.perform() + # Use send keys if input_keys true, else use javascript to set content + if input_keys: + action = ActionChains(self.driver) + action.send_keys(field_content) + action.perform() + else: + self.driver.execute_script( + "arguments[0].CodeMirror.setValue(arguments[1]);" + "arguments[0].CodeMirror.setCursor(" + "arguments[0].CodeMirror.lineCount(),0);", + codemirror_ele, field_content) def click_tab(self, tab_name): WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable( @@ -269,7 +352,15 @@ class PgadminPage: self.click_element(tab) - def wait_for_input_field_content(self, field_name, content): + def wait_for_input_by_element(self, element, content): + def input_field_has_content(driver): + return str(content) == element.get_attribute('value') + + return self._wait_for( + "field to contain '" + str(content) + "'", input_field_has_content + ) + + def wait_for_input_field_content(self, field_name, content, wait=1): def input_field_has_content(driver): element = driver.find_element_by_xpath( "//input[@name='" + field_name + "']") @@ -277,8 +368,8 @@ class PgadminPage: return str(content) == element.get_attribute('value') return self._wait_for( - "field to contain '" + str(content) + "'", input_field_has_content - ) + "field to contain '" + str(content) + "'", input_field_has_content, + wait) def wait_for_element(self, find_method_with_args): def element_if_it_exists(driver): @@ -286,7 +377,7 @@ class PgadminPage: element = find_method_with_args(driver) if element.is_displayed() and element.is_enabled(): return True - except NoSuchElementException: + except (NoSuchElementException, StaleElementReferenceException): return False self._wait_for("element to exist", element_if_it_exists) @@ -329,10 +420,10 @@ class PgadminPage: def wait_for_query_tool_loading_indicator_to_disappear(self): def spinner_has_disappeared(driver): try: - driver.find_element_by_xpath( - "//*[@id='fetching_data' and @class='hide']" + spinner = driver.find_element_by_css_selector( + "#editor-panel .pg-sp-container" ) - return False + return "d-none" in spinner.get_attribute("class") except NoSuchElementException: # wait for loading indicator disappear animation to complete. time.sleep(0.5) diff --git a/web/regression/requirements.txt b/web/regression/requirements.txt index 5199d32d..9093a3d0 100644 --- a/web/regression/requirements.txt +++ b/web/regression/requirements.txt @@ -16,7 +16,7 @@ python-mimeparse==1.6.0 testscenarios==0.5.0 testtools==2.3.0 traceback2==1.4.0 -selenium==3.11.0 +selenium==3.14.0 ############################################################### # Modules specifically required for Python3.3 or lesser version @@ -25,4 +25,4 @@ mock===2.0.0; python_version < '3.3' # Leave this at the end because there is a bug where the '--install-option' # is applied to all subsequent requirements -chromedriver_installer==0.0.6 --install-option='--chromedriver-version=2.45' +chromedriver_installer==0.0.6 --install-option='--chromedriver-version=2.46' diff --git a/web/regression/runtests.py b/web/regression/runtests.py index 48d3749f..6f95370f 100644 --- a/web/regression/runtests.py +++ b/web/regression/runtests.py @@ -230,9 +230,14 @@ def get_test_modules(arguments): if arguments['pkg'] is None or arguments['pkg'] == "all": TestsGeneratorRegistry.load_generators('pgadmin', exclude_pkgs) else: + for_modules = [] + if arguments['modules'] is not None: + for_modules = arguments['modules'].split(',') + TestsGeneratorRegistry.load_generators('pgadmin.%s' % arguments['pkg'], - exclude_pkgs) + exclude_pkgs, + for_modules) # Sort module list so that test suite executes the test cases sequentially module_list = TestsGeneratorRegistry.registry.items() @@ -263,6 +268,10 @@ def add_arguments(): '--default_browser', help='Executes the feature test in specific browser' ) + parser.add_argument( + '--modules', + help='Executes the feature test for specific modules in pkg' + ) arg = parser.parse_args() return arg