Hi, Please find attached updated patch. In this patch I have removed unused imports. Regarding ajax calls on slow network/computer I have already taken care of those. Before sending first version of patch I tested this on Murtuza's machine and also tested on slow machine as well.
Thanks, Harshal -- *Harshal Dhumal* *Sr. Software Engineer* EnterpriseDB India: http://www.enterprisedb.com The Enterprise PostgreSQL Company On Mon, Aug 7, 2017 at 8:55 AM, Sarah McAlear <smcal...@pivotal.io> wrote: > Hi Harshal! > > There are a few files in which there are leftover imports that are not > needed anymore: > > - PGDataypeFeatureTest > - PgadminPage > - CheckForXssFeatureTest > - CheckDebuggerForXssFeatureTest > > > We also noticed that there were quite a few time.sleep functions that > were removed. This seems great overall, but we think that some of them were > in place because of varying network and computer speeds. So for example, in > QueryToolFeatureTest, there is an ajax call that was followed by a > time.sleep to ensure that it had finished executing before continuing to > execute. Removing this may reintroduce some flakiness. If there are no > issues with flakiness after this patch, it seems like a great idea. We ran > the tests a few times and didn't notice any flakiness, but we're unsure if > it will be a problem on a different system. > > Thanks! > Wenlin & Sarah > > > On Wed, Aug 2, 2017 at 9:32 PM, Harshal Dhumal < > harshal.dhu...@enterprisedb.com> wrote: > >> Hi, >> >> Please find attached patch to improve feature test execution time. >> Now on my machine overall execution time is cut down to 280 seconds from >> 400+ seconds >> >> Changes: >> >> 1. Removed fixed python time.sleeps where ever possible. >> 2. Removed connect to server test cases. >> 3. Query tool test cases: >> i. Merged 3 test cases On demand result on scroll, grid select all >> and column select all. >> ii. Merged 3 test cases Explain query, Explain query with verbose >> and Explain query with cost. >> iii. Merged 3 test cases Explain analyze query, Explain analyze with >> buffers and Explain analyze with timing. >> 4. Improved debugger XSS test case execution time. >> >> -- >> *Harshal Dhumal* >> *Sr. Software Engineer* >> >> EnterpriseDB India: http://www.enterprisedb.com >> The Enterprise PostgreSQL Company >> > >
diff --git a/web/pgadmin/feature_tests/connect_to_server_feature_test.py b/web/pgadmin/feature_tests/connect_to_server_feature_test.py deleted file mode 100644 index 97e96f2..0000000 --- a/web/pgadmin/feature_tests/connect_to_server_feature_test.py +++ /dev/null @@ -1,84 +0,0 @@ -########################################################################## -# -# pgAdmin 4 - PostgreSQL Tools -# -# Copyright (C) 2013 - 2017, The pgAdmin Development Team -# This software is released under the PostgreSQL Licence -# -########################################################################## - -import time -from selenium.webdriver import ActionChains - -import config as app_config -from regression.feature_utils.base_feature_test import BaseFeatureTest -from regression.python_test_utils import test_utils - - -class ConnectsToServerFeatureTest(BaseFeatureTest): - """ - Tests that a database connection can be created from the UI - """ - scenarios = [ - ("Test database connection", dict()) - ] - - def before(self): - connection = test_utils.get_db_connection(self.server['db'], - self.server['username'], - self.server['db_password'], - self.server['host'], - self.server['port'], - self.server['sslmode']) - test_utils.drop_database(connection, "acceptance_test_db") - test_utils.create_database(self.server, "acceptance_test_db") - test_utils.create_table(self.server, "acceptance_test_db", "test_table") - - def runTest(self): - """This function tests that a database connection can be created from - the UI""" - self.assertEqual(app_config.APP_NAME, self.page.driver.title) - self.page.wait_for_spinner_to_disappear() - - self._connects_to_server() - self._tables_node_expandable() - - def after(self): - self.page.remove_server(self.server) - - connection = test_utils.get_db_connection(self.server['db'], - self.server['username'], - self.server['db_password'], - self.server['host'], - self.server['port'], - self.server['sslmode']) - test_utils.drop_database(connection, "acceptance_test_db") - - def _connects_to_server(self): - self.page.find_by_xpath("//*[@class='aciTreeText' and .='Servers']").click() - time.sleep(2) - self.page.driver.find_element_by_link_text("Object").click() - ActionChains(self.page.driver) \ - .move_to_element(self.page.driver.find_element_by_link_text("Create")) \ - .perform() - self.page.find_by_partial_link_text("Server...").click() - - server_config = self.server - self.page.fill_input_by_field_name("name", server_config['name']) - self.page.find_by_partial_link_text("Connection").click() - self.page.fill_input_by_field_name("host", server_config['host']) - self.page.fill_input_by_field_name("port", server_config['port']) - self.page.fill_input_by_field_name("username", server_config['username']) - self.page.fill_input_by_field_name("password", server_config['db_password']) - self.page.find_by_xpath("//button[contains(.,'Save')]").click() - - def _tables_node_expandable(self): - self.page.toggle_open_server(self.server['name']) - self.page.toggle_open_tree_item('Databases') - self.page.toggle_open_tree_item('acceptance_test_db') - # wait until all database dependant modules/js are loaded. - time.sleep(5) - 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_tree_item('test_table') 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 1809d45..d01bf66 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 @@ -8,7 +8,6 @@ ########################################################################## import pyperclip -import time from selenium.webdriver import ActionChains from selenium.webdriver.common.keys import Keys @@ -42,16 +41,16 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): self.page.toggle_open_tree_item(self.server['name']) self.page.toggle_open_tree_item('Databases') self.page.toggle_open_tree_item('acceptance_test_db') - time.sleep(5) - self.page.find_by_partial_link_text("Tools").click() - self.page.find_by_partial_link_text("Query Tool").click() - self.page.click_tab('Query -') - time.sleep(5) - ActionChains(self.page.driver).send_keys("SELECT * FROM test_table ORDER BY some_column").perform() - self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe")) + 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 test_table ORDER BY some_column") + self.page.find_by_id("btn-flash").click() - time.sleep(5) self._copies_rows() self._copies_columns() self._copies_row_using_keyboard_shortcut() diff --git a/web/pgadmin/feature_tests/pg_datatype_validation_test.py b/web/pgadmin/feature_tests/pg_datatype_validation_test.py index f700aab..63a3788 100644 --- a/web/pgadmin/feature_tests/pg_datatype_validation_test.py +++ b/web/pgadmin/feature_tests/pg_datatype_validation_test.py @@ -6,8 +6,7 @@ # This software is released under the PostgreSQL Licence # ########################################################################## -import time -from selenium.webdriver import ActionChains + from selenium.common.exceptions import TimeoutException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC @@ -38,7 +37,7 @@ class PGDataypeFeatureTest(BaseFeatureTest): def runTest(self): self.page.wait_for_spinner_to_disappear() - self._connects_to_server() + self.page.add_server(self.server) self._schema_node_expandable() # Check data types @@ -55,31 +54,6 @@ class PGDataypeFeatureTest(BaseFeatureTest): self.server['sslmode']) test_utils.drop_database(connection, "acceptance_test_db") - def _connects_to_server(self): - self.page.find_by_xpath( - "//*[@class='aciTreeText' and .='Servers']" - ).click() - time.sleep(2) - self.page.driver.find_element_by_link_text("Object").click() - ActionChains(self.page.driver) \ - .move_to_element( - self.page.driver.find_element_by_link_text("Create") - ).perform() - self.page.find_by_partial_link_text("Server...").click() - - server_config = self.server - self.page.fill_input_by_field_name("name", server_config['name']) - self.page.find_by_partial_link_text("Connection").click() - self.page.fill_input_by_field_name("host", server_config['host']) - self.page.fill_input_by_field_name("port", server_config['port']) - self.page.fill_input_by_field_name( - "username", server_config['username'] - ) - self.page.fill_input_by_field_name( - "password", server_config['db_password'] - ) - self.page.find_by_xpath("//button[contains(.,'Save')]").click() - def _schema_node_expandable(self): self.page.toggle_open_tree_item(self.server['name']) self.page.toggle_open_tree_item('Databases') diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index 4ffcff8..7e8532e 100644 --- a/web/pgadmin/feature_tests/query_tool_journey_test.py +++ b/web/pgadmin/feature_tests/query_tool_journey_test.py @@ -8,7 +8,6 @@ ########################################################################## import pyperclip -import time from selenium.webdriver import ActionChains from selenium.webdriver.common.keys import Keys @@ -39,7 +38,10 @@ class QueryToolJourneyTest(BaseFeatureTest): def runTest(self): self._navigate_to_query_tool() - self._execute_query("SELECT * FROM test_table ORDER BY value") + + self._execute_query( + "SELECT * FROM test_table ORDER BY value" + ) self._test_copies_rows() self._test_copies_columns() @@ -47,7 +49,6 @@ class QueryToolJourneyTest(BaseFeatureTest): def _test_copies_rows(self): pyperclip.copy("old clipboard contents") - time.sleep(5) self.page.driver.switch_to.default_content() self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe")) self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click() @@ -147,12 +148,9 @@ class QueryToolJourneyTest(BaseFeatureTest): self.page.toggle_open_tree_item('Databases') self.page.toggle_open_tree_item('acceptance_test_db') self.page.open_query_tool() - time.sleep(5) def _execute_query(self, query): - ActionChains(self.page.driver).send_keys(query).perform() - self.page.driver.switch_to.default_content() - self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe")) + self.page.fill_codemirror_area_with(query) self.page.find_by_id("btn-flash").click() def _assert_clickable(self, element): diff --git a/web/pgadmin/feature_tests/query_tool_tests.py b/web/pgadmin/feature_tests/query_tool_tests.py index 8e13817..c747beb 100644 --- a/web/pgadmin/feature_tests/query_tool_tests.py +++ b/web/pgadmin/feature_tests/query_tool_tests.py @@ -38,68 +38,28 @@ class QueryToolFeatureTest(BaseFeatureTest): test_utils.drop_database(connection, "acceptance_test_db") test_utils.create_database(self.server, "acceptance_test_db") self.page.wait_for_spinner_to_disappear() - self._connects_to_server() + self.page.add_server(self.server) self._locate_database_tree_node() self.page.open_query_tool() def runTest(self): # on demand result set on scrolling. - print("\nOn demand result set on scrolling... ", + print("\nOn demand query result... ", file=sys.stderr, end="") self._on_demand_result() - print("OK.", - file=sys.stderr) - self._clear_query_tool() - - # on demand result set on grid select all. - print("On demand result set on grid select all... ", - file=sys.stderr, end="") - self._on_demand_result_select_all_grid() - print("OK.", - file=sys.stderr) self._clear_query_tool() - # on demand result set on column select all. - print("On demand result set on column select all... ", + # explain query with verbose and cost + print("Explain query with verbose and cost... ", file=sys.stderr, end="") - self._on_demand_result_select_all_column() - print("OK.", - file=sys.stderr) - self._clear_query_tool() - - # explain query - print("Explain query... ", file=sys.stderr, end="") - self._query_tool_explain() - print("OK.", file=sys.stderr) - self._clear_query_tool() - - # explain query with verbose - print("Explain query with verbose... ", file=sys.stderr, end="") - self._query_tool_explain_verbose() + self._query_tool_explain_with_verbose_and_cost() print("OK.", file=sys.stderr) self._clear_query_tool() - # explain query with costs - print("Explain query with costs... ", file=sys.stderr, end="") - self._query_tool_explain_cost() - print("OK.", file=sys.stderr) - self._clear_query_tool() - - # explain analyze query - print("Explain analyze query... ", file=sys.stderr, end="") - self._query_tool_explain_analyze() - print("OK.", file=sys.stderr) - self._clear_query_tool() - - # explain analyze query with buffers - print("Explain analyze query with buffers... ", file=sys.stderr, end="") - self._query_tool_explain_analyze_buffers() - print("OK.", file=sys.stderr) - self._clear_query_tool() - - # explain analyze query with timing - print("Explain analyze query with timing... ", file=sys.stderr, end="") - self._query_tool_explain_analyze_timing() + # explain analyze query with buffers and timing + print("Explain analyze query with buffers and timing... ", + file=sys.stderr, end="") + self._query_tool_explain_analyze_with_buffers_and_timing() print("OK.", file=sys.stderr) self._clear_query_tool() @@ -137,32 +97,6 @@ class QueryToolFeatureTest(BaseFeatureTest): self.server['sslmode']) test_utils.drop_database(connection, "acceptance_test_db") - def _connects_to_server(self): - self.page.find_by_xpath( - "//*[@class='aciTreeText' and .='Servers']").click() - time.sleep(2) - self.page.driver.find_element_by_link_text("Object").click() - ActionChains(self.page.driver) \ - .move_to_element( - self.page.driver.find_element_by_link_text("Create"))\ - .perform() - self.page.find_by_partial_link_text("Server...").click() - - server_config = self.server - self.page.fill_input_by_field_name("name", server_config['name']) - self.page.find_by_partial_link_text("Connection").click() - self.page.fill_input_by_field_name("host", server_config['host']) - self.page.fill_input_by_field_name("port", server_config['port']) - self.page.fill_input_by_field_name( - "username", - server_config['username'] - ) - self.page.fill_input_by_field_name( - "password", - server_config['db_password'] - ) - self.page.find_by_xpath("//button[contains(.,'Save')]").click() - def _locate_database_tree_node(self): self.page.toggle_open_tree_item(self.server['name']) self.page.toggle_open_tree_item('Databases') @@ -180,14 +114,24 @@ class QueryToolFeatureTest(BaseFeatureTest): ) self.page.click_modal('Yes') + def _wait_until_canvas_updates(self, canvas, old_height): + # loop until canvas height changes or upto 5 seconds. + cur_time = time.time() + while canvas.size['height'] == old_height \ + and cur_time + 5 > time.time(): + pass + def _on_demand_result(self): ON_DEMAND_CHUNKS = 2 - query = """-- On demand query result on scroll -SELECT generate_series(1, {}) as id""".format( + row_id_to_find = config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS + + query = """-- On demand query result on scroll, grid select all, column select all +SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS) + print("\nOn demand result set on scrolling... ", + file=sys.stderr, end="") wait = WebDriverWait(self.page.driver, 10) - time.sleep(1) self.page.fill_codemirror_area_with(query) self.page.find_by_id("btn-flash").click() @@ -197,154 +141,83 @@ SELECT generate_series(1, {}) as id""".format( canvas = wait.until(EC.presence_of_element_located( (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))) + old_height = canvas.size['height'] # scroll to bottom to fetch next chunk of result set. self.driver.execute_script( "pgAdmin.SqlEditor.jquery('.slick-viewport').scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas').height());" ) - # wait for ajax to complete. - time.sleep(1) - # again scroll to bottom to bring last row of next chunk in - # viewport. - self.driver.execute_script( - "pgAdmin.SqlEditor.jquery('.slick-viewport').scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas').height());" - ) - - row_id_to_find = config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS - - canvas.find_element_by_xpath( - '//span[text()="{}"]'.format(row_id_to_find) - ) - - def _on_demand_result_select_all_grid(self): - ON_DEMAND_CHUNKS = 3 - query = """-- On demand query result on grid select all -SELECT generate_series(1, {}) as id""".format( - config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS) + self._wait_until_canvas_updates(canvas, old_height) - wait = WebDriverWait(self.page.driver, 10) - - self.page.fill_codemirror_area_with(query) + self._check_ondemand_result(row_id_to_find, canvas) + print("OK.", file=sys.stderr) + print("On demand result set on grid select all... ", + file=sys.stderr, end="") self.page.find_by_id("btn-flash").click() self.page.wait_for_query_tool_loading_indicator_to_disappear() - wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, ".slick-header-column"))).click() - - # wait for until all records are fetched and selected. - time.sleep(1) - # scroll to bottom to bring last row of next chunk in - # viewport. - self.driver.execute_script( - "pgAdmin.SqlEditor.jquery('.slick-viewport').scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas').height());" - ) - canvas = wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) - ) - - row_id_to_find = config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS + (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))) - canvas.find_element_by_xpath( - '//span[text()="{}"]'.format(row_id_to_find) - ) + old_height = canvas.size['height'] - def _on_demand_result_select_all_column(self): - ON_DEMAND_CHUNKS = 4 - query = """-- On demand query result on column select all -SELECT generate_series(1, {}) as id1, 'dummy' as id2""".format( - config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS) + wait.until(EC.presence_of_element_located( + (By.CSS_SELECTOR, ".slick-header-column"))).click() - wait = WebDriverWait(self.page.driver, 10) + self._wait_until_canvas_updates(canvas, old_height) - self.page.fill_codemirror_area_with(query) + self._check_ondemand_result(row_id_to_find, canvas) + print("OK.", file=sys.stderr) + print("On demand result set on column select all... ", + file=sys.stderr, end="") self.page.find_by_id("btn-flash").click() self.page.wait_for_query_tool_loading_indicator_to_disappear() - # click on first data column to select all column. + canvas = wait.until(EC.presence_of_element_located( + (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))) + + old_height = canvas.size['height'] + # click on first data column to select all column. wait.until(EC.presence_of_element_located( ( By.XPATH, "//span[contains(@class, 'column-name') and contains(., 'id1')]")) ).click() - # wait for until all records are fetched and selected. - time.sleep(1) - # scroll to bottom to bring last row of next chunk in - # viewport. + self._wait_until_canvas_updates(canvas, old_height) + + self._check_ondemand_result(row_id_to_find, canvas) + print("OK.", file=sys.stderr) + + def _check_ondemand_result(self, row_id_to_find, canvas): + # scroll to bottom to bring last row of next chunk in viewport. self.driver.execute_script( "pgAdmin.SqlEditor.jquery('.slick-viewport').scrollTop(pgAdmin.SqlEditor.jquery('.grid-canvas').height());" ) - canvas = wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) - ) - - row_id_to_find = config.ON_DEMAND_RECORD_COUNT * ON_DEMAND_CHUNKS - canvas.find_element_by_xpath( '//span[text()="{}"]'.format(row_id_to_find) ) - def _query_tool_explain(self): - query = """-- Explain query + def _query_tool_explain_with_verbose_and_cost(self): + query = """-- Explain query with verbose and cost SELECT generate_series(1, 1000) as id order by id desc""" wait = WebDriverWait(self.page.driver, 10) self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-query-dropdown").click() - self.page.find_by_id("btn-explain").click() - - self.page.wait_for_query_tool_loading_indicator_to_disappear() - - self.page.click_tab('Data Output') - - canvas = wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) - ) - # Search for Plan word in result - canvas.find_element_by_xpath("//*[contains(string(),'Plan')]") - - def _query_tool_explain_verbose(self): - query = """-- Explain query with verbose -SELECT generate_series(1, 1000) as id order by id desc""" - - wait = WebDriverWait(self.page.driver, 10) - self.page.fill_codemirror_area_with(query) query_op = self.page.find_by_id("btn-query-dropdown") query_op.click() ActionChains(self.driver).move_to_element( query_op.find_element_by_xpath( "//li[contains(.,'Explain Options')]")).perform() - self.page.find_by_id("btn-explain-verbose").click() - self.page.find_by_id("btn-explain").click() - self.page.wait_for_query_tool_loading_indicator_to_disappear() - self.page.click_tab('Data Output') - canvas = wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) - ) - # Search for 'Output' word in result - canvas.find_element_by_xpath("//*[contains(string(), 'Output')]") - def _query_tool_explain_cost(self): - query = """-- Explain query with costs -SELECT generate_series(1, 1000) as id order by id desc""" - - wait = WebDriverWait(self.page.driver, 10) - - self.page.fill_codemirror_area_with(query) - query_op = self.page.find_by_id("btn-query-dropdown") - query_op.click() - - ActionChains(self.driver).move_to_element( - query_op.find_element_by_xpath( - "//li[contains(.,'Explain Options')]")).perform() + self.page.find_by_id("btn-explain-verbose").click() self.page.find_by_id("btn-explain-costs").click() @@ -357,32 +230,15 @@ SELECT generate_series(1, 1000) as id order by id desc""" canvas = wait.until(EC.presence_of_element_located( (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) ) - # Search for 'Total Cost word in result - canvas.find_element_by_xpath("//*[contains(string(),'Total Cost')]") - def _query_tool_explain_analyze(self): - query = """-- Explain analyze query -SELECT generate_series(1, 1000) as id order by id desc""" - - wait = WebDriverWait(self.page.driver, 10) - - self.page.fill_codemirror_area_with(query) - - self.page.find_by_id("btn-query-dropdown").click() - self.page.find_by_id("btn-explain-analyze").click() - - self.page.wait_for_query_tool_loading_indicator_to_disappear() - - self.page.click_tab('Data Output') + # Search for 'Output' word in result (verbose option) + canvas.find_element_by_xpath("//*[contains(string(), 'Output')]") - canvas = wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) - ) - # Search for Actual Rows word in result - canvas.find_element_by_xpath("//*[contains(string(),'Actual Rows')]") + # Search for 'Total Cost' word in result (cost option) + canvas.find_element_by_xpath("//*[contains(string(),'Total Cost')]") - def _query_tool_explain_analyze_buffers(self): - query = """-- Explain analyze query with buffers + def _query_tool_explain_analyze_with_buffers_and_timing(self): + query = """-- Explain analyze query with buffers and timing SELECT generate_series(1, 1000) as id order by id desc""" wait = WebDriverWait(self.page.driver, 10) @@ -398,32 +254,6 @@ SELECT generate_series(1, 1000) as id order by id desc""" self.page.find_by_id("btn-explain-buffers").click() - self.page.find_by_id("btn-explain-analyze").click() - - self.page.wait_for_query_tool_loading_indicator_to_disappear() - - self.page.click_tab('Data Output') - - canvas = wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) - ) - # Search for 'Shared Read Blocks' word in result - canvas.find_element_by_xpath("//*[contains(string(), 'Shared Read Blocks')]") - - def _query_tool_explain_analyze_timing(self): - query = """-- Explain analyze query with timing -SELECT generate_series(1, 1000) as id order by id desc""" - - wait = WebDriverWait(self.page.driver, 10) - - self.page.fill_codemirror_area_with(query) - query_op = self.page.find_by_id("btn-query-dropdown") - query_op.click() - - ActionChains(self.driver).move_to_element( - query_op.find_element_by_xpath( - "//li[contains(.,'Explain Options')]")).perform() - self.page.find_by_id("btn-explain-timing").click() self.page.find_by_id("btn-explain-analyze").click() @@ -435,8 +265,15 @@ SELECT generate_series(1, 1000) as id order by id desc""" canvas = wait.until(EC.presence_of_element_located( (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas")) ) - # Search for 'Actual Total Time' word in result - canvas.find_element_by_xpath("//*[contains(string(), 'Actual Total Time')]") + # Search for 'Shared Read Blocks' word in result (buffers option) + canvas.find_element_by_xpath( + "//*[contains(string(), 'Shared Read Blocks')]" + ) + + # Search for 'Actual Total Time' word in result (timing option) + canvas.find_element_by_xpath( + "//*[contains(string(), 'Actual Total Time')]" + ) def _query_tool_auto_commit_disabled(self): table_name = 'query_tool_auto_commit_disabled_table' diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/pgadmin/feature_tests/view_data_dml_queries.py index e8cf598..0b2da0c 100644 --- a/web/pgadmin/feature_tests/view_data_dml_queries.py +++ b/web/pgadmin/feature_tests/view_data_dml_queries.py @@ -199,8 +199,6 @@ CREATE TABLE public.defaults self.page.toggle_open_tree_item(self.server['name']) self.page.toggle_open_tree_item('Databases') self.page.toggle_open_tree_item('acceptance_test_db') - # wait until all database dependant modules/js are loaded. - time.sleep(5) self.page.toggle_open_tree_item('Schemas') self.page.toggle_open_tree_item('public') self.page.toggle_open_tree_item('Tables') @@ -267,7 +265,7 @@ CREATE TABLE public.defaults cell_xpath = CheckForViewDataTest._get_cell_xpath( 'r'+str(idx), 1 ) - time.sleep(0.4) + time.sleep(0.2) self._update_cell(cell_xpath, config_data[str(idx)]) self.page.find_by_id("btn-save").click() # Save data @@ -300,4 +298,4 @@ CREATE TABLE public.defaults if idx != 1 and not is_new_row: self.assertEquals(cells[idx], config_data[str(idx)][1]) elif is_new_row: - self.assertEquals(cells[idx], config_data[str(idx)][1]) \ No newline at end of file + self.assertEquals(cells[idx], config_data[str(idx)][1]) 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 30b1051..ef2ab5d 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 @@ -7,13 +7,9 @@ # ########################################################################## -from selenium.webdriver import ActionChains from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.by import By -import time + class CheckForXssFeatureTest(BaseFeatureTest): """ @@ -53,7 +49,7 @@ class CheckForXssFeatureTest(BaseFeatureTest): def runTest(self): self.page.wait_for_spinner_to_disappear() - self._connects_to_server() + self.page.add_server(self.server) self._tables_node_expandable() self._check_xss_in_browser_tree() self._check_xss_in_properties_tab() @@ -65,7 +61,6 @@ class CheckForXssFeatureTest(BaseFeatureTest): self.page.close_query_tool() def after(self): - time.sleep(1) self.page.remove_server(self.server) connection = test_utils.get_db_connection(self.server['db'], self.server['username'], @@ -75,24 +70,6 @@ class CheckForXssFeatureTest(BaseFeatureTest): self.server['sslmode']) test_utils.drop_database(connection, "acceptance_test_db") - def _connects_to_server(self): - self.page.find_by_xpath("//*[@class='aciTreeText' and .='Servers']").click() - time.sleep(2) - self.page.driver.find_element_by_link_text("Object").click() - ActionChains(self.page.driver) \ - .move_to_element(self.page.driver.find_element_by_link_text("Create")) \ - .perform() - self.page.find_by_partial_link_text("Server...").click() - - server_config = self.server - self.page.fill_input_by_field_name("name", server_config['name']) - self.page.find_by_partial_link_text("Connection").click() - self.page.fill_input_by_field_name("host", server_config['host']) - self.page.fill_input_by_field_name("port", server_config['port']) - self.page.fill_input_by_field_name("username", server_config['username']) - self.page.fill_input_by_field_name("password", server_config['db_password']) - self.page.find_by_xpath("//button[contains(.,'Save')]").click() - def _tables_node_expandable(self): self.page.toggle_open_server(self.server['name']) self.page.toggle_open_tree_item('Databases') @@ -155,10 +132,10 @@ class CheckForXssFeatureTest(BaseFeatureTest): 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 -') - self.page.fill_codemirror_area_with("select '<img src=\"x\" onerror=\"console.log(1)\">'") - time.sleep(1) + self.page.fill_codemirror_area_with( + "select '<img src=\"x\" onerror=\"console.log(1)\">'" + ) self.page.find_by_id("btn-flash").click() - wait = WebDriverWait(self.page.driver, 5) result_row = self.page.find_by_xpath( "//*[contains(@class, 'ui-widget-content') and contains(@style, 'top:0px')]" @@ -177,9 +154,4 @@ class CheckForXssFeatureTest(BaseFeatureTest): def _check_escaped_characters(self, source_code, string_to_find, source): # For XSS we need to search against element's html code - if source_code.find(string_to_find) == -1: - # No escaped characters found - assert False, "{0} might be vulnerable to XSS ".format(source) - else: - # escaped characters found - assert True + assert source_code.find(string_to_find) != -1, "{0} might be vulnerable to XSS ".format(source) diff --git a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py index e847040..158d919 100644 --- a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py +++ b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py @@ -8,9 +8,13 @@ ########################################################################## from selenium.webdriver import ActionChains +from selenium.common.exceptions import TimeoutException from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest -import time +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.by import By + class CheckDebuggerForXssFeatureTest(BaseFeatureTest): """Tests to check if Debugger is vulnerable to XSS.""" @@ -30,34 +34,15 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest): def runTest(self): self.page.wait_for_spinner_to_disappear() - self._connects_to_server() + self.page.add_server(self.server) self._function_node_expandable() self._debug_function() def after(self): - time.sleep(0.5) test_utils.drop_debug_function(self.server, "postgres", "test_function") self.page.remove_server(self.server) - def _connects_to_server(self): - self.page.find_by_xpath("//*[@class='aciTreeText' and .='Servers']").click() - time.sleep(2) - self.page.driver.find_element_by_link_text("Object").click() - ActionChains(self.page.driver) \ - .move_to_element(self.page.driver.find_element_by_link_text("Create")) \ - .perform() - self.page.find_by_partial_link_text("Server...").click() - - server_config = self.server - self.page.fill_input_by_field_name("name", server_config['name']) - self.page.find_by_partial_link_text("Connection").click() - self.page.fill_input_by_field_name("host", server_config['host']) - self.page.fill_input_by_field_name("port", server_config['port']) - self.page.fill_input_by_field_name("username", server_config['username']) - self.page.fill_input_by_field_name("password", server_config['db_password']) - self.page.find_by_xpath("//button[contains(.,'Save')]").click() - def _function_node_expandable(self): self.page.toggle_open_server(self.server['name']) self.page.toggle_open_tree_item('Databases') @@ -73,32 +58,44 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest): .move_to_element(self.page.driver.find_element_by_link_text("Debugging")) \ .perform() self.page.driver.find_element_by_link_text("Debug").click() - time.sleep(0.5) + # We need to check if debugger plugin is installed or not try: - is_error = self.page.find_by_xpath( - "//div[contains(@class,'ajs-header')]" - ).text - except Exception as e: + wait = WebDriverWait(self.page.driver, 2) + is_error = wait.until(EC.presence_of_element_located( + (By.XPATH, "//div[contains(@class,'ajs-header')]")) + ) + except TimeoutException as e: is_error = None # If debugger plugin is not found - if is_error and is_error == "Debugger Error": + if is_error and is_error.text == "Debugger Error": self.page.click_modal('OK') - self.skipTest("Please make sure that debugger plugin is properly configured") + self.skipTest( + "Please make sure that debugger plugin is properly configured" + ) else: - time.sleep(2) - self.page.driver.switch_to.frame(self.page.driver.find_element_by_tag_name('iframe')) - self.page.click_element(self.page.driver.find_elements_by_xpath("//button")[2]) - time.sleep(2) + self.page.driver.switch_to.frame( + self.page.driver.find_element_by_tag_name('iframe') + ) + + wait.until(EC.presence_of_element_located( + (By.XPATH, "//span[contains(.,'Hello, pgAdmin4')]")) + ) + self.page.click_element( + self.page.driver.find_elements_by_xpath("//button")[2] + ) - # Only this tab is vulnerable rest are BackGrid & Code Mirror control - # which are already tested in Query tool test case + wait.until(EC.presence_of_element_located( + (By.XPATH, "//td[contains(@class,'test_function') and contains(.,'Hello, pgAdmin4')]")) + ) + + # Only this tab is vulnerable rest are BackGrid & Code Mirror + # control which are already tested in Query tool test case self.page.click_tab("Messages") source_code = self.page.find_by_xpath( "//*[@id='messages']" ).get_attribute('innerHTML') - self._check_escaped_characters( source_code, 'NOTICE: <img src="x" onerror="console.log(1)">', @@ -107,18 +104,12 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest): self._close_debugger() def _close_debugger(self): - time.sleep(0.5) self.page.driver.switch_to_default_content() - time.sleep(0.5) self.page.click_element( self.page.find_by_xpath("//*[@id='dockerContainer']/div/div[3]/div/div[2]/div[1]") ) def _check_escaped_characters(self, source_code, string_to_find, source): # For XSS we need to search against element's html code - if source_code.find(string_to_find) == -1: - # No escaped characters found - assert False, "{0} might be vulnerable to XSS ".format(source) - else: - # escaped characters found - assert True + # No escaped characters found + assert source_code.find(string_to_find) != -1, "{0} might be vulnerable to XSS ".format(source) diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index 21e45a6..030d05e 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -8,10 +8,9 @@ ########################################################################## import time -import math from selenium.common.exceptions import NoSuchElementException, \ - WebDriverException, StaleElementReferenceException + WebDriverException from selenium.webdriver import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as EC @@ -180,9 +179,6 @@ class PgadminPage: self.wait_for_input_field_content(field_name, field_content) def fill_codemirror_area_with(self, field_content): - # For long text, if we try to execute send_keys and perform back to back, then the actions are - # not executed properly as the driver can send only 50 to 60 characters. To avoid this, sleep - # on the basis of content length. def find_codemirror(driver): try: driver.switch_to.default_content() @@ -203,8 +199,6 @@ class PgadminPage: action = ActionChains(self.driver) action.send_keys(field_content) action.perform() - sleep_time = math.ceil(len(field_content) / 50) - time.sleep(sleep_time) def click_tab(self, tab_name): tab = self.find_by_xpath("//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') "