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/browser_tool_bar_test.py b/web/pgadmin/feature_tests/browser_tool_bar_test.py index 944363dc..82486059 100644 --- a/web/pgadmin/feature_tests/browser_tool_bar_test.py +++ b/web/pgadmin/feature_tests/browser_tool_bar_test.py @@ -82,7 +82,7 @@ class BrowserToolBarFeatureTest(BaseFeatureTest): self.page.select_tree_item(self.test_db) 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.test_table_name) retry_count = 0 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..46673f10 --- /dev/null +++ b/web/pgadmin/feature_tests/locators.py @@ -0,0 +1,22 @@ +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_rollback_check_status = "#btn-auto-rollback > i" + btn_auto_commit = "#btn-auto-commit" + btn_auto_commit_check_status = "#btn-auto-commit > i" + 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_datatype_validation_test.py b/web/pgadmin/feature_tests/pg_datatype_validation_test.py index 7d1ed698..8a5b293b 100644 --- a/web/pgadmin/feature_tests/pg_datatype_validation_test.py +++ b/web/pgadmin/feature_tests/pg_datatype_validation_test.py @@ -153,7 +153,14 @@ class PGDataypeFeatureTest(BaseFeatureTest): self.page.find_by_id("btn-flash").click() wait = WebDriverWait(self.page.driver, 5) - wait.until(EC.presence_of_element_located( + + # wait for the visibility of the grid to appear + wait.until(EC.visibility_of_element_located( + (By.XPATH, + "//*[contains(@class,'column-type')]" + ) + )) + wait.until(EC.visibility_of_element_located( (By.XPATH, "//*[contains(@class,'column-type') and " "contains(.,'{}')]".format(batch['datatype'][0]) 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..ffdda7bf 100644 --- a/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py @@ -68,21 +68,30 @@ 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") + # .input-group-append >button + 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() 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 = self.page.find_by_css_selector( - ".pg-bg-status .bg-success-light .pg-bg-status-text").text + ".pg-bg-status-text").text + + print("Debug: .pg-bg-status-text %s"%status) + + #.pg-bg-status-text self.assertEquals(status, "Successfully completed.") 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 +108,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 +125,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() @@ -125,7 +134,7 @@ 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 + ".pg-bg-status-text").text self.assertEquals(status, "Successfully completed.") self.page.find_by_css_selector( diff --git a/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py b/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py index a03000b1..09589dd3 100644 --- a/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_maintenance_test.py @@ -84,7 +84,7 @@ class PGUtilitiesMaintenanceFeatureTest(BaseFeatureTest): def _verify_command(self): status = self.page.find_by_css_selector( - ".pg-bg-status .bg-success-light .pg-bg-status-text").text + ".pg-bg-status-text").text self.assertEquals(status, "Successfully completed.") self.page.find_by_css_selector(".pg-bg-more-details").click() command = self.page.find_by_css_selector( diff --git a/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py b/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py index f008ca8f..4f6caf6c 100644 --- a/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py +++ b/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py @@ -21,7 +21,6 @@ class QueryToolAutoCompleteFeatureTest(BaseFeatureTest): """ This feature test will test the query tool auto complete feature. """ - first_schema_name = "" second_schema_name = "" first_table_name = "" @@ -175,6 +174,20 @@ class QueryToolAutoCompleteFeatureTest(BaseFeatureTest): self.page.fill_codemirror_area_with(word) ActionChains(self.page.driver).key_down( Keys.CONTROL).send_keys(Keys.SPACE).key_up(Keys.CONTROL).perform() - self.page.find_by_xpath( + + # if IntelliSense is present then verify this + if self.page.check_if_element_exist_by_xpath\ + ("//ul[@class='CodeMirror-hints default']", 2): + self.page.find_by_xpath( "//ul[contains(@class, 'CodeMirror-hints') and " "contains(., '" + expected_string + "')]") + else: + # if no IntelliSense is present it means there is only one option + # so check if required string is present in codeMirror + code_mirror = self.page.find_by_xpath( + "//pre[@class=' CodeMirror-line ']/span") + code_mirror_text = code_mirror.text + + if not expected_string in code_mirror_text: + raise Exception("Required String %s is not " + "present"%expected_string) diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index d863f8e8..f13e6fad 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,44 +74,51 @@ 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\" " "does not exist", failed_history_detail_pane.text ) - ActionChains(self.page.driver) \ - .send_keys(Keys.ARROW_DOWN) \ - .perform() + self.page.wait_for_element(lambda driver: driver + .find_element_by_css_selector + ("#query_list> .query-group>ul>li")) + + # get the query history rows and click the previous query row which + # was executed and verify it + history_rows = self.driver.find_elements_by_css_selector( + "#query_list> .query-group>ul>li") + print("the number of history_rows are 10 %s"%len(history_rows)) + history_rows[1].click() + selected_history_entry = self.page.find_by_css_selector( "#query_list .selected") self.assertIn(("SELECT * FROM %s ORDER BY value" % self.test_table_name), selected_history_entry.text) - query_element = self.page.driver.\ - find_element_by_xpath( - "//div[@id='history_grid']//div[@class='entry selected']" - "/div[@class='query']") - - self.assertIn(("SELECT * FROM %s ORDER BY value" - % self.test_table_name), query_element.text) - + # check second(invalid) query also exist in the history tab with error newly_selected_history_entry = self.page.find_by_xpath( - "//*[@id='query_list']/ul/li[2]") + "//*[@id='query_list']/div/ul/li[1]") self.page.click_element(newly_selected_history_entry) - selected_history_detail_pane = self.page.find_by_id("query_detail") + + selected_invalid_history_entry = self.page.find_by_css_selector( + "#query_list .selected .entry.error .query") + self.assertIn("SELECT * FROM table_that_doesnt_exist", - selected_history_detail_pane.get_attribute('innerHTML')) + selected_invalid_history_entry.text) self.page.click_tab("Query Editor") self.__clear_query_tool() @@ -118,13 +126,14 @@ 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") query_we_need_to_scroll_to = self.page.find_by_xpath( - "//*[@id='query_list']/ul/li[17]") + "//*[@id='query_list']/div/ul/li[17]") self.page.click_element(query_we_need_to_scroll_to) @@ -140,12 +149,13 @@ 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") query_we_need_to_scroll_to = self.page.find_by_xpath( - "//*[@id='query_list']/ul/li[17]" + "//*[@id='query_list']/div/ul/li[17]" ) for _ in range(17): ActionChains(self.page.driver) \ @@ -173,7 +183,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..7f50f1e4 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,12 @@ 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 for header of the table to be visible + wait.until(EC.visibility_of_element_located( + (By.XPATH,'//div[@class="slick-header-columns"]'))) wait.until(EC.presence_of_element_located( (By.XPATH, @@ -192,8 +204,13 @@ 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 for header of the table to be visible + wait.until(EC.visibility_of_element_located( + (By.XPATH,'//div[@class="slick-header-columns"]'))) + # wait for first row to contain value wait.until(EC.presence_of_element_located( (By.XPATH, '//span[@data-row="0" and text()="1"]')) @@ -210,10 +227,15 @@ 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() + # wait for header of the table to be visible + wait.until(EC.visibility_of_element_located( + (By.XPATH,'//div[@class="slick-header-columns"]'))) + wait.until(EC.presence_of_element_located( (By.XPATH, '//span[@data-row="0" and text()="1"]')) @@ -252,16 +274,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 +310,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 +352,18 @@ CREATE TABLE public.{}();""".format(table_name) self.page.fill_codemirror_area_with(query) - query_op = self.page.find_by_id("btn-query-dropdown") + # open auto commit option and disable it + 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() + # close option query_op.click() - self.page.find_by_id("btn-flash").click() + # execute query + 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') @@ -340,6 +372,7 @@ CREATE TABLE public.{}();""".format(table_name) 'contains(string(), "CREATE TABLE")]' ) + # do the ROLLBACK and check if the table is present or not self._clear_query_tool() query = """-- 1. (Done) Disable auto commit. -- 2. (Done) Create table in public schema. @@ -347,7 +380,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 +398,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') @@ -379,6 +414,18 @@ SELECT relname FROM pg_class "and without any explicit commit.".format( table_name ) + # again roll back so that the auto commit drop down is enabled + query = """-- 1. (Done) Disable auto commit. + -- 2. (Done) Create table in public schema. + -- 3. ROLLBACK transaction. + -- 4. Check if table is *NOT* created. + ROLLBACK;""" + self.page.fill_codemirror_area_with(query) + self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_execute_query).click() + + self.page.wait_for_query_tool_loading_indicator_to_disappear() + def _query_tool_auto_commit_enabled(self): @@ -393,14 +440,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 +466,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 +484,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 +503,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 +532,18 @@ 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_id("btn-auto-commit").click() + # uncheckt auto commit and check auto-rollback + self.uncheck_execute_option('auto_commit') + self.check_execute_option('auto_rollback') 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 +559,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 +578,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 +598,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 +618,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,41 +643,27 @@ SELECT 1, pg_sleep(300)""" self.page.fill_codemirror_area_with(query) - query_op = self.page.find_by_id("btn-query-dropdown") - query_op.click() - - auto_rollback_btn = self.page.find_by_id("btn-auto-rollback") - - auto_rollback_check = auto_rollback_btn.find_element_by_tag_name("i") + # query_button drop can be disabled so enable + commit_button = self.page.find_by_css_selector("#btn-commit") + if not commit_button.get_attribute('disabled'): + commit_button.click() - # if auto rollback is enabled then 'i' element will - # have 'auto-rollback fa fa-check' classes - # if auto rollback is disabled then 'i' element will - # have 'auto-rollback fa fa-check visibility-hidden' classes - - if 'auto-rollback fa fa-check' == str( - auto_rollback_check.get_attribute('class')): - auto_rollback_btn.click() - - auto_commit_btn = self.page.find_by_id("btn-auto-commit") - - auto_commit_check = auto_commit_btn.find_element_by_tag_name("i") - - # if auto commit is enabled then 'i' element will - # have 'auto-commit fa fa-check' classes - # if auto commit is disabled then 'i' element will - # have 'auto-commit fa fa-check visibility-hidden' classes - - if 'auto-commit fa fa-check visibility-hidden' == str( - auto_commit_check.get_attribute('class')): - auto_commit_btn.click() + query_op = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_query_dropdown) + query_op.click() + # enable auto-commit and disable auto-rollback + self.check_execute_option('auto_commit') + self.uncheck_execute_option('auto_rollback') + # close drop down 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 +688,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 +702,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 +717,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') @@ -690,39 +738,62 @@ SELECT 1, pg_sleep(300)""" self.server['port'], self.server['sslmode'] ) - pg_cursor = connection.cursor() pg_cursor.execute('select version()') version_string = pg_cursor.fetchone() + # check if jit is turned on + jit_enabled = False + try: + pg_cursor.execute('show jit') + show_jit = pg_cursor.fetchone() + if show_jit[0] == 'on': + jit_enabled = True + except: + pass + is_edb = False if len(version_string) > 0: is_edb = 'EnterpriseDB' in version_string[0] connection.close() - return connection.server_version >= 110000 and not is_edb + return connection.server_version >= 110000 and jit_enabled def _query_tool_explain_check_jit_stats(self): 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() + # disable Explain options and only enable COST option + 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() + # click cost button + cost_btn = self.page.find_by_css_selector( + QueryToolLocatorsCss.btn_explain_costs) + cost_btn.click() + # close explain options 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') @@ -735,6 +806,38 @@ SELECT 1, pg_sleep(300)""" self._clear_query_tool() + 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( + QueryToolLocatorsCss.btn_auto_commit_check_status) + if 'visibility-hidden' in check_status.get_attribute('class'): + self.page.find_by_css_selector(QueryToolLocatorsCss. + btn_auto_commit).click() + if option == 'auto_rollback': + check_status = self.driver.find_element_by_css_selector( + QueryToolLocatorsCss.btn_auto_rollback_check_status) + if 'visibility-hidden' in check_status.get_attribute('class'): + self.page.find_by_css_selector(QueryToolLocatorsCss. + 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( + QueryToolLocatorsCss.btn_auto_commit_check_status) + if 'visibility-hidden' not in check_status.get_attribute('class'): + self.page.find_by_css_selector(QueryToolLocatorsCss. + btn_auto_commit).click() + if option == 'auto_rollback': + check_status = self.driver.find_element_by_css_selector( + QueryToolLocatorsCss.btn_auto_rollback_check_status) + if 'visibility-hidden' not in check_status.get_attribute('class'): + self.page.find_by_css_selector(QueryToolLocatorsCss. + btn_auto_rollback).click() + class WaitForAnyElementWithText(object): def __init__(self, locator, text): diff --git a/web/pgadmin/feature_tests/test_data.json b/web/pgadmin/feature_tests/test_data.json index 03aca9aa..50374c6c 100644 --- a/web/pgadmin/feature_tests/test_data.json +++ b/web/pgadmin/feature_tests/test_data.json @@ -9,7 +9,7 @@ "6": ["\\\"\\\"", "\"\"", "text", "double backslash followed by a double quote"], "7": ["\\\\\"\\\\\"", "\\\\\"\\\\\"", "text", "double backslash followed by a double quote"], "8": ["", "[null]", "text"], - "9": ["", "[51,52]", "json"], + "9": ["", "[51, 52]", "json"], "10": ["[61,62]", "[61,62]", "json"], "11": ["", "true", "bool"], "12": ["", "[null]", "bool"], @@ -18,7 +18,7 @@ "15": ["{}", "{}", "text[]"], "16": ["{data,NULL,'',\"\"}", "{data,NULL,'',\"\"}", "text[]"], "17": ["{}", "{}", "int[]"], - "18": ["{123,,456}", "{123,NULL,456}", "int[]"], + "18": ["{123,123,456}", "{123,123,456}", "int[]"], "19": ["", "[null]", "boolean[]"], "20": ["{false,null,true}", "{f,NULL,t}", "boolean[]"] } diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/pgadmin/feature_tests/view_data_dml_queries.py index 1be9dcfa..8e69ffc5 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 @@ -182,15 +182,15 @@ CREATE TABLE public.defaults_{0} cell_el.find_element_by_css_selector('input').clear() else: ActionChains(self.driver).send_keys(value).perform() - elif cell_type in ['text', 'json', 'text[]', 'boolean[]']: - self.page.find_by_xpath( - "//*[contains(@class, 'pg_textarea')]").click() - ActionChains(self.driver).send_keys(value).perform() + text_area_ele = self.page.find_by_css_selector( + ".pg-text-editor > textarea") + text_area_ele.click() + text_area_ele.send_keys(value) # Click on editor's Save button self.page.find_by_css_selector( - '.pg_text_editor button[data-label="Save"]').click() + '.btn.btn-primary.long_text_editor').click() else: # Boolean editor test for to True click if data[1] == 'true': @@ -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 912c99be..b22034e0 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', @@ -518,17 +511,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..e09ceac1 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,54 @@ 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(.6) + 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_tables_node(self): + """The function will be used for opening Trees node only""" + + # get the element which contains 'aria-expanded' info + tables_expansion_ele =self.find_by_xpath("//div[div[div[div[div[div" + "[div[div[span[span[" + "(@class='aciTreeText') and " + "text()='Tables']]]]]]]]]]") + + if tables_expansion_ele.get_attribute('aria-expanded') =='false': + # button element of the Tables node to open it + item_button = self.find_by_xpath( + "//div[span[span[(@class='aciTreeText') and text()" + "='Tables']]]/span[@class='aciTreeButton']") + ActionChains(self.driver).click(item_button).perform() def toggle_open_server(self, tree_item_text): def check_for_password_dialog_or_tree_open(driver): @@ -224,38 +259,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 +369,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 +385,20 @@ 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 check_if_element_exist_by_xpath(self, xpath, timeout=5): + """This function will verify if an element exist and on that basis + will return True or False. Will handle exception internally""" + element_found = False + try: + WebDriverWait(self.driver, timeout, .01).until( + EC.visibility_of_element_located((By.XPATH, xpath))) + element_found = True + except: + pass + return element_found def wait_for_element(self, find_method_with_args): def element_if_it_exists(driver): @@ -286,7 +406,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 +449,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/python_test_utils/test_gui_helper.py b/web/regression/python_test_utils/test_gui_helper.py index b1d3b25f..2e8bffcb 100644 --- a/web/regression/python_test_utils/test_gui_helper.py +++ b/web/regression/python_test_utils/test_gui_helper.py @@ -17,22 +17,18 @@ def close_bgprocess_popup(tester): # 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 > div > " - "div > div > i"): + ".ajs-message.ajs-bg-bgprocess.ajs-visible"): tester.driver.find_element_by_css_selector( - ".ajs-message.ajs-bg-bgprocess.ajs-visible >div >div " - ">div>i").click() + ".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click() except Exception: pass # In cases where restore div is not closed (sometime due to some error) try: if tester.driver.find_element_by_xpath( - "//div[contains(text(), 'Process Watcher - " - "Restoring backup')]"): - tester.driver.find_element_by_xpath( - "//div[div[div[div[contains(text(), 'Process Watcher " - "- Restoring backup')]]]]" - "/following-sibling::div/div/div").click() + "//div[@class='card-header bg-primary d-flex']/div[contains(" + "text(), 'Restoring backup')]"): + tester.driver.find_element_by_css_selector( + ".btn.btn-sm-sq.btn-primary.pg-bg-close > i").click() except Exception: pass 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 f757decb..92b86e0f 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