Hi, Last week Sarah, Matt and I had call regarding some issues with feature test cases. For example in auto commit disable test scenario from query tool test cases by removing code line *self.page.find_by_id("btn-auto-commit").click() *the test case scenario was not falling. This issue was due to previously on going database transaction. This issue was also with test scenarios like auto commit enable and auto rollback enabled. The attached patch fixes this issue.
@Sarah and Matt can you please give this patch a try to check if issues we discussed last week are fixed. Thanks, Harshal -- *Harshal Dhumal* *Sr. Software Engineer* EnterpriseDB India: http://www.enterprisedb.com The Enterprise PostgreSQL Company On Fri, Aug 11, 2017 at 11:55 AM, Sarah McAlear <smcal...@pivotal.io> wrote: > Great job on cleaning up the whole feature test suite! This is really > going to benefit all of the developers a lot. > > Thanks, > Matt & Sarah > > On Fri, Aug 11, 2017 at 12:24 PM, Harshal Dhumal < > harshal.dhu...@enterprisedb.com> wrote: > >> 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..5fdd630 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() - 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() + self._query_tool_explain_with_verbose_and_cost() 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') @@ -182,169 +116,105 @@ class QueryToolFeatureTest(BaseFeatureTest): 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() - self.page.wait_for_query_tool_loading_indicator_to_disappear() - - canvas = wait.until(EC.presence_of_element_located( - (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))) + # self.page.wait_for_query_tool_loading_indicator_to_disappear() - # 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.until(EC.presence_of_element_located( + (By.XPATH, + '//span[@data-row="0" and text()="1"]')) ) - # wait for ajax to complete. - time.sleep(1) - # again scroll to bottom to bring last row of next chunk in - # viewport. + # 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());" ) - 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) - - wait = WebDriverWait(self.page.driver, 10) + canvas = wait.until(EC.presence_of_element_located( + (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))) - 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() + # 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 - - canvas.find_element_by_xpath( - '//span[text()="{}"]'.format(row_id_to_find) + (By.XPATH, + '//span[@data-row="0" and text()="1"]')) ) - 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) + canvas = wait.until(EC.presence_of_element_located( + (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))) - 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() + # self.page.wait_for_query_tool_loading_indicator_to_disappear() - # click on first data column to select all column. + wait.until(EC.presence_of_element_located( + (By.XPATH, + '//span[@data-row="0" and text()="1"]')) + ) + # 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. + canvas = wait.until(EC.presence_of_element_located( + (By.CSS_SELECTOR, "#datagrid .slick-viewport .grid-canvas"))) + + 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 +227,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 +251,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 +262,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' @@ -451,18 +285,7 @@ CREATE TABLE public.{}();""".format(table_name) self.page.find_by_id("btn-query-dropdown").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' == str(auto_commit_check.get_attribute( - 'class')): - auto_commit_btn.click() + self.page.find_by_id("btn-auto-commit").click() self.page.find_by_id("btn-flash").click() self.page.wait_for_query_tool_loading_indicator_to_disappear() @@ -503,33 +326,43 @@ SELECT relname FROM pg_class WHERE relkind IN ('r','s','t') and relnamespace = 2 assert len(el) == 0, "Table '{}' created with auto commit disabled and without any explicit commit.".format(table_name) def _query_tool_auto_commit_enabled(self): - table_name = 'query_tool_auto_commit_enabled_table' - query = """-- 1. END any open transaction. --- 2. Enable auto commit. + + query = """-- 1. Enable auto commit. +-- 2. END any open transaction. -- 3. Create table in public schema. -- 4. ROLLBACK transaction -- 5. Check if table is created event after ROLLBACK. -END; -CREATE TABLE public.{}();""".format(table_name) - wait = WebDriverWait(self.page.driver, 10) +END;""" self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-query-dropdown").click() + wait = WebDriverWait(self.page.driver, 10) - auto_commit_btn = self.page.find_by_id("btn-auto-commit") + btn_query_dropdown = wait.until(EC.presence_of_element_located( + (By.ID, "btn-query-dropdown"))) - auto_commit_check = auto_commit_btn.find_element_by_tag_name("i") + btn_query_dropdown.click() - # 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 + self.page.find_by_id("btn-auto-commit").click() + + self.page.find_by_id("btn-flash").click() + + self.page.wait_for_query_tool_loading_indicator_to_disappear() + + self._clear_query_tool() + + table_name = 'query_tool_auto_commit_enabled_table' + query = """-- 1. (Done) END any open transaction. +-- 2. Enable auto commit. +-- 3. Create table in public schema. +-- 4. ROLLBACK transaction +-- 5. Check if table is created event after ROLLBACK. +CREATE TABLE public.{}();""".format(table_name) + + self.page.fill_codemirror_area_with(query) - if 'auto-commit fa fa-check visibility-hidden' == str(auto_commit_check.get_attribute( - 'class')): - auto_commit_btn.click() self.page.find_by_id("btn-flash").click() + self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') self.driver.find_element_by_xpath( @@ -572,8 +405,8 @@ SELECT relname FROM pg_class WHERE relkind IN ('r','s','t') and relnamespace = 2 def _query_tool_auto_rollback_enabled(self): table_name = 'query_tool_auto_rollback_enabled_table' - query = """-- 1. END any open transaction. --- 2. Enable auto rollback and disable auto commit. + query = """-- 1. Enable auto rollback and disable auto commit. +-- 2. END any open transaction. -- 3. Create table in public schema. -- 4. Generate error in transaction. -- 5. END transaction. @@ -581,6 +414,12 @@ SELECT relname FROM pg_class WHERE relkind IN ('r','s','t') and relnamespace = 2 END;""" self.page.fill_codemirror_area_with(query) + self.page.find_by_id("btn-query-dropdown").click() + + self.page.find_by_id("btn-auto-rollback").click() + + self.page.find_by_id("btn-auto-commit").click() + self.page.find_by_id("btn-flash").click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self._clear_query_tool() @@ -596,34 +435,6 @@ CREATE TABLE public.{}();""".format(table_name) self.page.fill_codemirror_area_with(query) - self.page.find_by_id("btn-query-dropdown").click() - - auto_rollback_btn = self.page.find_by_id("btn-auto-rollback") - - auto_rollback_check = auto_rollback_btn.find_element_by_tag_name("i") - - # 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 visibility-hidden' == 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' == str(auto_commit_check.get_attribute( - 'class')): - auto_commit_btn.click() - self.page.find_by_id("btn-flash").click() self.page.wait_for_query_tool_loading_indicator_to_disappear() self.page.click_tab('Messages') 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..c927cfe 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,11 @@ 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 + 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_roles_control_test.py b/web/pgadmin/feature_tests/xss_checks_roles_control_test.py index 1adcac0..ea84b5a 100644 --- a/web/pgadmin/feature_tests/xss_checks_roles_control_test.py +++ b/web/pgadmin/feature_tests/xss_checks_roles_control_test.py @@ -11,6 +11,7 @@ from selenium.webdriver import ActionChains from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest + class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): """Tests to check role membership control for xss.""" @@ -22,9 +23,9 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): def before(self): # Some test function is needed for debugger test_utils.create_role(self.server, "postgres", - "test_role") + "test_role") test_utils.create_role(self.server, "postgres", - "<h1>test</h1>") + "<h1>test</h1>") def runTest(self): self.page.wait_for_spinner_to_disappear() @@ -34,9 +35,9 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): def after(self): test_utils.drop_role(self.server, "postgres", - "test_role") + "test_role") test_utils.drop_role(self.server, "postgres", - "<h1>test</h1>") + "<h1>test</h1>") self.page.remove_server(self.server) def _connects_to_server(self): @@ -77,12 +78,6 @@ class CheckRoleMembershipControlFeatureTest(BaseFeatureTest): ) self.page.find_by_xpath("//button[contains(.,'Cancel')]").click() - 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/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') "