From 2d58f60a53aadc07bf471102778e1089fd2239a2 Mon Sep 17 00:00:00 2001 From: Yogesh Mahajan Date: Thu, 27 May 2021 11:01:25 +0530 Subject: [PATCH] Modifies the way to execute feature tests in parallel and it should be configured in Server Mode. --- .../feature_tests/browser_tool_bar_test.py | 2 + .../feature_tests/file_manager_test.py | 24 +- .../feature_tests/table_ddl_feature_test.py | 4 +- web/pgadmin/utils/route.py | 3 + web/regression/README | 3 +- .../feature_utils/base_feature_test.py | 4 +- web/regression/feature_utils/pgadmin_page.py | 23 +- .../python_test_utils/test_utils.py | 28 +- web/regression/runtests.py | 254 ++++++++++-------- 9 files changed, 209 insertions(+), 136 deletions(-) diff --git a/web/pgadmin/feature_tests/browser_tool_bar_test.py b/web/pgadmin/feature_tests/browser_tool_bar_test.py index 6ca88c5d0..a4544daf1 100644 --- a/web/pgadmin/feature_tests/browser_tool_bar_test.py +++ b/web/pgadmin/feature_tests/browser_tool_bar_test.py @@ -88,6 +88,7 @@ class BrowserToolBarFeatureTest(BaseFeatureTest): BrowserToolBarLocators.view_table_data_button_css), (By.CSS_SELECTOR, BrowserToolBarLocators.view_data_panel_css)), 'View/Edit Data tab did not open after clicking View/Edit button.') + self.page.close_query_tool(prompt=False) def test_filtered_rows_tool_button(self): self.assertTrue(self.page.retry_click( @@ -96,3 +97,4 @@ class BrowserToolBarFeatureTest(BaseFeatureTest): (By.CSS_SELECTOR, BrowserToolBarLocators.filter_alertify_box_css)), 'Filter dialogue did not open on clicking filter button.') self.page.click_modal('Cancel') + self.page.close_query_tool(prompt=False) diff --git a/web/pgadmin/feature_tests/file_manager_test.py b/web/pgadmin/feature_tests/file_manager_test.py index 832c96b0e..ac9f25c9f 100644 --- a/web/pgadmin/feature_tests/file_manager_test.py +++ b/web/pgadmin/feature_tests/file_manager_test.py @@ -41,7 +41,10 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): self.wait = WebDriverWait(self.page.driver, 10) filename = self.server_information['type'] + \ str(self.server_information['server_version']) - self.XSS_FILE = '/tmp/.sql' + if self.parallel_ui_tests: + self.XSS_FILE = '/.sql' + else: + self.XSS_FILE = '/tmp/.sql' # Remove any previous file if os.path.isfile(self.XSS_FILE): os.remove(self.XSS_FILE) @@ -92,21 +95,14 @@ class CheckFileManagerFeatureTest(BaseFeatureTest): self.page.fill_input_by_css_selector( QueryToolLocators.input_file_path_css, "/tmp", key_after_input=Keys.RETURN) + time.sleep(2) - if self.page.driver.capabilities['browserName'] == 'firefox': - table = self.page.wait_for_element_to_reload( - lambda driver: driver.find_element_by_css_selector( - QueryToolLocators.select_file_content_css) - ) - else: - self.wait.until(EC.visibility_of_element_located( - (By.CSS_SELECTOR, QueryToolLocators.select_file_content_css))) - self.wait.until(lambda element: - self.page.driver.find_element_by_css_selector( - '[name=home]').is_enabled()) + self.wait.until(EC.visibility_of_element_located( + (By.CSS_SELECTOR, QueryToolLocators.select_file_content_css))) + + table = self.page.driver.find_element_by_css_selector( + QueryToolLocators.select_file_content_css) - table = self.page.driver.find_element_by_css_selector( - QueryToolLocators.select_file_content_css) retry_count = 0 while retry_count < 5: try: diff --git a/web/pgadmin/feature_tests/table_ddl_feature_test.py b/web/pgadmin/feature_tests/table_ddl_feature_test.py index 0cedcf6f9..d191019ca 100644 --- a/web/pgadmin/feature_tests/table_ddl_feature_test.py +++ b/web/pgadmin/feature_tests/table_ddl_feature_test.py @@ -46,8 +46,8 @@ class TableDdlFeatureTest(BaseFeatureTest): # Wait till data is displayed in SQL Tab self.assertTrue(self.page.check_if_element_exist_by_xpath( "//*[contains(@class,'CodeMirror-lines') and " - "contains(.,'CREATE TABLE public.%s')]" % self.test_table_name, - 10), "No data displayed in SQL tab") + "contains(.,'CREATE TABLE IF NOT EXISTS public.%s')]" + % self.test_table_name, 10), "No data displayed in SQL tab") def after(self): self.page.remove_server(self.server) diff --git a/web/pgadmin/utils/route.py b/web/pgadmin/utils/route.py index 2896a85b5..d61a97b49 100644 --- a/web/pgadmin/utils/route.py +++ b/web/pgadmin/utils/route.py @@ -148,6 +148,9 @@ class BaseTestGenerator(unittest.TestCase): def setDriver(self, driver): self.driver = driver + def setParallelUI_tests(self, parallel_ui_tests): + self.parallel_ui_tests = parallel_ui_tests + def setServerInformation(self, server_information): self.server_information = server_information diff --git a/web/regression/README b/web/regression/README index c52ef9478..a7e4230a2 100644 --- a/web/regression/README +++ b/web/regression/README @@ -142,7 +142,8 @@ Python Tests: 'pgadmin4/web/pgadmin/utils/test.py' file. - To run Feature Tests in parallel using selenoid(grid + docker), selenoid - need to be installed. Steps to install selenoid - + need to be installed nad should be run only with SERVER_MODE=True. + Steps to install selenoid - - Install & Start docker $yum -y install docker docker-registry diff --git a/web/regression/feature_utils/base_feature_test.py b/web/regression/feature_utils/base_feature_test.py index 4cdcfe287..acecc05ab 100644 --- a/web/regression/feature_utils/base_feature_test.py +++ b/web/regression/feature_utils/base_feature_test.py @@ -27,7 +27,7 @@ class BaseFeatureTest(BaseTestGenerator): def setUp(self): self.server = deepcopy(self.server) self.server['name'] += ' Feature Tests' - if app_config.SERVER_MODE: + if app_config.SERVER_MODE and not self.parallel_ui_tests: self.skipTest( "Currently, config is set to start pgadmin in server mode. " "This test doesn't know username and password so doesn't work " @@ -36,6 +36,8 @@ class BaseFeatureTest(BaseTestGenerator): self.page = PgadminPage(self.driver, app_config) try: + if self.parallel_ui_tests: + self.page.login_to_app(self.server['login_details']) test_utils.reset_layout_db() self.page.driver.switch_to.default_content() self.page.wait_for_app() diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index 726033470..8778cf0b9 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -37,6 +37,18 @@ class PgadminPage: self.timeout = 30 self.app_start_timeout = 90 + def login_to_app(self, user_detail): + if not (self.check_if_element_exist_by_xpath( + '//a[@id="navbar-user"]', 1)): + user_edt_box_el = self.driver.find_element_by_name('email') + user_edt_box_el.send_keys(user_detail['login_username']) + password_edt_box_el = self.driver.find_element_by_name('password') + password_edt_box_el.send_keys(user_detail['login_password']) + submit_btn = self.driver.find_element_by_xpath( + '//button[@value="Login"]') + submit_btn.click() + self.wait_for_spinner_to_disappear() + def reset_layout(self): attempt = 0 while attempt < 4: @@ -57,7 +69,15 @@ class PgadminPage: self.wait_for_reloading_indicator_to_disappear() def refresh_page(self): - self.driver.refresh() + try: + self.driver.refresh() + # wait until alert is present + WebDriverWait(self.driver, 1).until(EC.alert_is_present()) + + # switch to alert and accept it + self.driver.switch_to.alert.accept() + except TimeoutException: + pass def click_modal(self, button_text): time.sleep(0.5) @@ -300,6 +320,7 @@ class PgadminPage: delete_menu_item = self.find_by_partial_link_text("Remove Server") self.click_element(delete_menu_item) self.click_modal('Yes') + time.sleep(1) def select_tree_item(self, tree_item_text): item = self.find_by_xpath( diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py index 4de3ad9d1..be4cd273b 100644 --- a/web/regression/python_test_utils/test_utils.py +++ b/web/regression/python_test_utils/test_utils.py @@ -17,6 +17,7 @@ import psycopg2 import sqlite3 import shutil from functools import partial +import random from selenium.webdriver.support.wait import WebDriverWait from testtools.testcase import clone_test_with_new_id @@ -1400,7 +1401,7 @@ def get_parallel_sequential_module_list(module_list): sequential_tests_file = [ 'pgadmin.feature_tests.pg_utilities_backup_restore_test', 'pgadmin.feature_tests.pg_utilities_maintenance_test', - 'pgadmin.feature_tests.keyboard_shortcut_test'] + ] # list of tests can be executed in parallel parallel_tests = list(module_list) @@ -1704,3 +1705,28 @@ def create_user_wise_test_client(user): return wrapper return multi_user_decorator + + +def create_users_for_parallel_tests(tester): + """ + Function creates user using /user api + @param tester: test client + @return: uer details dict + """ + login_username = 'ui_test_user' + str(random.randint(1000, 9999)) +\ + '@edb.com' + user_details = {'login_username': login_username, + 'login_password': 'adminedb'} + response = tester.post( + '/user_management/user/', + data=json.dumps(dict(username=user_details['login_username'], + email=user_details['login_username'], + newPassword=user_details['login_password'], + confirmPassword=user_details['login_password'], + active=True, + role="1" + )), + follow_redirects=True) + user_id = json.loads(response.data.decode('utf-8'))['id'] + user_details['user_id'] = user_id + return user_details diff --git a/web/regression/runtests.py b/web/regression/runtests.py index 1142fb9cf..f59dacacb 100644 --- a/web/regression/runtests.py +++ b/web/regression/runtests.py @@ -151,7 +151,7 @@ scenarios.apply_scenario = test_utils.apply_scenario def get_suite(module_list, test_server, test_app_client, server_information, - test_db_name, driver_passed): + test_db_name, driver_passed, parallel_ui_test): """ This function add the tests to test suite and return modified test suite variable. @@ -164,6 +164,12 @@ def get_suite(module_list, test_server, test_app_client, server_information, :type test_app_client: pgadmin app object :return pgadmin_suite: test suite with test cases :rtype: TestSuite + :param driver_passed: driver object to run selenium tests + :type driver_passed: webdriver object + :param parallel_ui_test: whether ui tests to be run in parallel + :type parallel_ui_test: boolan + :param test_db_name: database name + :type test_db_name: string """ modules = [] pgadmin_suite = unittest.TestSuite() @@ -182,11 +188,11 @@ def get_suite(module_list, test_server, test_app_client, server_information, obj.setTestClient(test_app_client) obj.setTestServer(test_server) obj.setDriver(driver_passed) + obj.setParallelUI_tests(parallel_ui_test) obj.setServerInformation(server_information) obj.setTestDatabaseName(test_db_name) scenario = scenarios.generate_scenarios(obj) pgadmin_suite.addTests(scenario) - return pgadmin_suite @@ -450,12 +456,14 @@ class StreamToLogger(object): pass -def execute_test(test_module_list_passed, server_passed, driver_passed): +def execute_test(test_module_list_passed, server_passed, driver_passed, + parallel_ui_test=False): """ Function executes actually test - :param test_module_list_passed: - :param server_passed: - :param driver_passed: + :param test_module_list_passed: test modules + :param server_passed: serve details + :param driver_passed: webdriver object + :param parallel_ui_test: parallel ui tests :return: """ try: @@ -493,11 +501,17 @@ def execute_test(test_module_list_passed, server_passed, driver_passed): test_utils.configure_preferences( default_binary_path=server_passed['default_binary_paths']) + # Create user to run selenoid tests in parallel + if parallel_ui_test: + server_passed['login_details'] = \ + test_utils.create_users_for_parallel_tests(test_client) + # Get unit test suit suite = get_suite(test_module_list_passed, server_passed, test_client, - server_information, test_db_name, driver_passed) + server_information, test_db_name, driver_passed, + parallel_ui_test=parallel_ui_test) # Run unit test suit created tests = unittest.TextTestRunner(stream=sys.stderr, @@ -570,12 +584,11 @@ def run_parallel_tests(url_client, servers_details, parallel_tests_lists, :param max_thread_count: """ driver_object = None - - # Thread list - threads_list = [] - # Create thread for each server - for ser in servers_details: - try: + try: + # Thread list + threads_list = [] + # Create thread for each server + for ser in servers_details: while True: # If active thread count <= max_thread_count, add new thread if threading.activeCount() <= max_thread_count: @@ -594,33 +607,33 @@ def run_parallel_tests(url_client, servers_details, parallel_tests_lists, # Start thread t = threading.Thread(target=execute_test, name=thread_name, args=(parallel_tests_lists, ser, - driver_object)) + driver_object, True)) threads_list.append(t) t.start() - time.sleep(3) + time.sleep(10) break # else sleep for 10 seconds else: time.sleep(10) - # Start threads in parallel - for t in threads_list: - t.join() + # Start threads in parallel + for t in threads_list: + t.join() - except Exception as exc: - # Print exception stack trace - traceback.print_exc(file=sys.stderr) - print('Exception before starting tests for ' + ser['name'], - file=sys.stderr) - print(str(exc), file=sys.stderr) + except Exception as exc: + # Print exception stack trace + traceback.print_exc(file=sys.stderr) + print('Exception before starting tests for ' + ser['name'], + file=sys.stderr) + print(str(exc), file=sys.stderr) - # Mark failure as true - global failure - failure = True + # Mark failure as true + global failure + failure = True - # Clean driver object created - if driver_object is not None: - driver_object.quit() + # Clean driver object created + if driver_object is not None: + driver_object.quit() def run_sequential_tests(url_client, servers_details, sequential_tests_lists, @@ -654,7 +667,7 @@ def run_sequential_tests(url_client, servers_details, sequential_tests_lists, t = threading.Thread(target=execute_test, name=thread_name, args=(sequential_tests_lists, ser, - driver_object)) + driver_object, True)) t.start() t.join() except Exception as exc: @@ -779,105 +792,114 @@ if __name__ == '__main__': if args['pkg'] is not None: node_name = args['pkg'].split('.')[-1] + is_parallel_ui_tests = test_utils.is_parallel_ui_tests(args) # Check if feature tests included & parallel tests switch passed - if test_utils.is_feature_test_included(args) and \ - test_utils.is_parallel_ui_tests(args): - try: - # Get selenium config dict - selenoid_config = test_setup.config_data['selenoid_config'] + if test_utils.is_feature_test_included(args) and is_parallel_ui_tests: + if config.SERVER_MODE: + try: + # Get selenium config dict + selenoid_config = test_setup.config_data['selenoid_config'] - # Set DEFAULT_SERVER value - default_server = selenoid_config['pgAdmin_default_server'] - os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"] = str(default_server) - config.DEFAULT_SERVER = str(default_server) + # Set DEFAULT_SERVER value + default_server = selenoid_config['pgAdmin_default_server'] + os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"] = str( + default_server) + config.DEFAULT_SERVER = str(default_server) - # Get hub url - hub_url = selenoid_config['selenoid_url'] + # Get hub url + hub_url = selenoid_config['selenoid_url'] - # Get selenium grid status & list of available browser out passed - selenium_grid_status, list_of_browsers \ - = test_utils.get_selenium_grid_status_and_browser_list(hub_url, - args) + # Get selenium grid status & list of available browser + # out of passed + selenium_grid_status, list_of_browsers = test_utils.\ + get_selenium_grid_status_and_browser_list(hub_url, args) - # Execute tests if selenium-grid is up - if selenium_grid_status and len(list_of_browsers) > 0: - app_starter_local = None - # run across browsers - for browser_info in list_of_browsers: - try: - # browser info - browser_name, browser_version = \ - test_utils.get_browser_details(browser_info, - hub_url) + # Execute tests if selenium-grid is up + if selenium_grid_status and len(list_of_browsers) > 0: + app_starter_local = None + # run across browsers + for browser_info in list_of_browsers: + try: + # browser info + browser_name, browser_version = \ + test_utils.get_browser_details(browser_info, + hub_url) - # test lists can be executed in parallel & sequentially - parallel_tests, sequential_tests = \ - test_utils.get_parallel_sequential_module_list( - test_module_list) + # test lists can be executed in + # parallel & sequentially + parallel_tests, sequential_tests = \ + test_utils.get_parallel_sequential_module_list( + test_module_list) - # Print test summary - test_utils.print_test_summary( - test_module_list, parallel_tests, sequential_tests, - browser_name, browser_version) - - # Create app form source code - app_starter_local = AppStarter(None, config) - client_url = app_starter_local.start_app() - - if config.DEBUG: - print('pgAdmin is launched with DEBUG=True, ' - 'hence sleeping for 50 seconds.', - file=sys.stderr) - time.sleep(50) - - # Running Parallel tests - if len(parallel_tests) > 0: - parallel_sessions = \ - int(selenoid_config['max_parallel_sessions']) - - run_parallel_tests( - client_url, servers_info, parallel_tests, - browser_name, browser_version, - parallel_sessions) - - # Sequential Tests - if len(sequential_tests) > 0: - run_sequential_tests( - client_url, servers_info, sequential_tests, + # Print test summary + test_utils.print_test_summary( + test_module_list, parallel_tests, + sequential_tests, browser_name, browser_version) - # Clean up environment - if app_starter_local: - app_starter_local.stop_app() + # Create app form source code + app_starter_local = AppStarter(None, config) + client_url = app_starter_local.start_app() - # Pause before printing result in order - # not to mix output - time.sleep(5) + if config.DEBUG: + print('pgAdmin is launched with DEBUG=True, ' + 'hence sleeping for 50 seconds.', + file=sys.stderr) + time.sleep(50) - # Print note for completion of execution in a browser. - print( - "\n============= Test execution with {0} is " - "completed.=============".format(browser_name), - file=sys.stderr) - print_test_results() + # Running Parallel tests + if len(parallel_tests) > 0: + parallel_sessions = \ + int(selenoid_config[ + 'max_parallel_sessions']) - except SystemExit: - if app_starter_local: - app_starter_local.stop_app() - if handle_cleanup: - handle_cleanup() - raise - else: - print( - "\n============= Either Selenium Grid is NOT up OR" - " browser list is 0 =============", file=sys.stderr) + run_parallel_tests( + client_url, servers_info, parallel_tests, + browser_name, browser_version, + parallel_sessions) + + # Sequential Tests + if len(sequential_tests) > 0: + run_sequential_tests( + client_url, servers_info, sequential_tests, + browser_name, browser_version) + + # Clean up environment + if app_starter_local: + app_starter_local.stop_app() + + # Pause before printing result in order + # not to mix output + time.sleep(5) + + print( + "\n============= Test execution with {0} is " + "completed.=============".format(browser_name), + file=sys.stderr) + print_test_results() + + except SystemExit: + if app_starter_local: + app_starter_local.stop_app() + if handle_cleanup: + handle_cleanup() + raise + else: + print( + "\n============= Either Selenium Grid is NOT up OR" + " browser list is 0 =============", file=sys.stderr) + failure = True + except Exception as exc: + # Print exception stack trace + traceback.print_exc(file=sys.stderr) + print(str(exc)) failure = True - except Exception as exc: - # Print exception stack trace - traceback.print_exc(file=sys.stderr) - print(str(exc)) + del os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"] + else: + print( + "\n============= Please Turn on Server Mode to run selenoid " + "tests =============", file=sys.stderr) failure = True - del os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"] else: try: for server in servers_info: