Modifies the way to execute feature tests in parallel and it should be configured in Server Mode.

This commit is contained in:
Yogesh Mahajan 2021-05-27 11:01:25 +05:30 committed by Akshay Joshi
parent faa49687be
commit 2d58f60a53
9 changed files with 209 additions and 136 deletions

View File

@ -88,6 +88,7 @@ class BrowserToolBarFeatureTest(BaseFeatureTest):
BrowserToolBarLocators.view_table_data_button_css), BrowserToolBarLocators.view_table_data_button_css),
(By.CSS_SELECTOR, BrowserToolBarLocators.view_data_panel_css)), (By.CSS_SELECTOR, BrowserToolBarLocators.view_data_panel_css)),
'View/Edit Data tab did not open after clicking View/Edit button.') '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): def test_filtered_rows_tool_button(self):
self.assertTrue(self.page.retry_click( self.assertTrue(self.page.retry_click(
@ -96,3 +97,4 @@ class BrowserToolBarFeatureTest(BaseFeatureTest):
(By.CSS_SELECTOR, BrowserToolBarLocators.filter_alertify_box_css)), (By.CSS_SELECTOR, BrowserToolBarLocators.filter_alertify_box_css)),
'Filter dialogue did not open on clicking filter button.') 'Filter dialogue did not open on clicking filter button.')
self.page.click_modal('Cancel') self.page.click_modal('Cancel')
self.page.close_query_tool(prompt=False)

View File

@ -41,6 +41,9 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
self.wait = WebDriverWait(self.page.driver, 10) self.wait = WebDriverWait(self.page.driver, 10)
filename = self.server_information['type'] + \ filename = self.server_information['type'] + \
str(self.server_information['server_version']) str(self.server_information['server_version'])
if self.parallel_ui_tests:
self.XSS_FILE = '/<img src=x ' + filename + '=alert("1")>.sql'
else:
self.XSS_FILE = '/tmp/<img src=x ' + filename + '=alert("1")>.sql' self.XSS_FILE = '/tmp/<img src=x ' + filename + '=alert("1")>.sql'
# Remove any previous file # Remove any previous file
if os.path.isfile(self.XSS_FILE): if os.path.isfile(self.XSS_FILE):
@ -92,21 +95,14 @@ class CheckFileManagerFeatureTest(BaseFeatureTest):
self.page.fill_input_by_css_selector( self.page.fill_input_by_css_selector(
QueryToolLocators.input_file_path_css, QueryToolLocators.input_file_path_css,
"/tmp", key_after_input=Keys.RETURN) "/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( self.wait.until(EC.visibility_of_element_located(
(By.CSS_SELECTOR, QueryToolLocators.select_file_content_css))) (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())
table = self.page.driver.find_element_by_css_selector( table = self.page.driver.find_element_by_css_selector(
QueryToolLocators.select_file_content_css) QueryToolLocators.select_file_content_css)
retry_count = 0 retry_count = 0
while retry_count < 5: while retry_count < 5:
try: try:

View File

@ -46,8 +46,8 @@ class TableDdlFeatureTest(BaseFeatureTest):
# Wait till data is displayed in SQL Tab # Wait till data is displayed in SQL Tab
self.assertTrue(self.page.check_if_element_exist_by_xpath( self.assertTrue(self.page.check_if_element_exist_by_xpath(
"//*[contains(@class,'CodeMirror-lines') and " "//*[contains(@class,'CodeMirror-lines') and "
"contains(.,'CREATE TABLE public.%s')]" % self.test_table_name, "contains(.,'CREATE TABLE IF NOT EXISTS public.%s')]"
10), "No data displayed in SQL tab") % self.test_table_name, 10), "No data displayed in SQL tab")
def after(self): def after(self):
self.page.remove_server(self.server) self.page.remove_server(self.server)

View File

@ -148,6 +148,9 @@ class BaseTestGenerator(unittest.TestCase):
def setDriver(self, driver): def setDriver(self, driver):
self.driver = driver self.driver = driver
def setParallelUI_tests(self, parallel_ui_tests):
self.parallel_ui_tests = parallel_ui_tests
def setServerInformation(self, server_information): def setServerInformation(self, server_information):
self.server_information = server_information self.server_information = server_information

View File

@ -142,7 +142,8 @@ Python Tests:
'pgadmin4/web/pgadmin/utils/test.py' file. 'pgadmin4/web/pgadmin/utils/test.py' file.
- To run Feature Tests in parallel using selenoid(grid + docker), selenoid - 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 - Install & Start docker
$yum -y install docker docker-registry $yum -y install docker docker-registry

View File

@ -27,7 +27,7 @@ class BaseFeatureTest(BaseTestGenerator):
def setUp(self): def setUp(self):
self.server = deepcopy(self.server) self.server = deepcopy(self.server)
self.server['name'] += ' Feature Tests' self.server['name'] += ' Feature Tests'
if app_config.SERVER_MODE: if app_config.SERVER_MODE and not self.parallel_ui_tests:
self.skipTest( self.skipTest(
"Currently, config is set to start pgadmin in server mode. " "Currently, config is set to start pgadmin in server mode. "
"This test doesn't know username and password so doesn't work " "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) self.page = PgadminPage(self.driver, app_config)
try: try:
if self.parallel_ui_tests:
self.page.login_to_app(self.server['login_details'])
test_utils.reset_layout_db() test_utils.reset_layout_db()
self.page.driver.switch_to.default_content() self.page.driver.switch_to.default_content()
self.page.wait_for_app() self.page.wait_for_app()

View File

@ -37,6 +37,18 @@ class PgadminPage:
self.timeout = 30 self.timeout = 30
self.app_start_timeout = 90 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): def reset_layout(self):
attempt = 0 attempt = 0
while attempt < 4: while attempt < 4:
@ -57,7 +69,15 @@ class PgadminPage:
self.wait_for_reloading_indicator_to_disappear() self.wait_for_reloading_indicator_to_disappear()
def refresh_page(self): def refresh_page(self):
try:
self.driver.refresh() 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): def click_modal(self, button_text):
time.sleep(0.5) time.sleep(0.5)
@ -300,6 +320,7 @@ class PgadminPage:
delete_menu_item = self.find_by_partial_link_text("Remove Server") delete_menu_item = self.find_by_partial_link_text("Remove Server")
self.click_element(delete_menu_item) self.click_element(delete_menu_item)
self.click_modal('Yes') self.click_modal('Yes')
time.sleep(1)
def select_tree_item(self, tree_item_text): def select_tree_item(self, tree_item_text):
item = self.find_by_xpath( item = self.find_by_xpath(

View File

@ -17,6 +17,7 @@ import psycopg2
import sqlite3 import sqlite3
import shutil import shutil
from functools import partial from functools import partial
import random
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support.wait import WebDriverWait
from testtools.testcase import clone_test_with_new_id from testtools.testcase import clone_test_with_new_id
@ -1400,7 +1401,7 @@ def get_parallel_sequential_module_list(module_list):
sequential_tests_file = [ sequential_tests_file = [
'pgadmin.feature_tests.pg_utilities_backup_restore_test', 'pgadmin.feature_tests.pg_utilities_backup_restore_test',
'pgadmin.feature_tests.pg_utilities_maintenance_test', 'pgadmin.feature_tests.pg_utilities_maintenance_test',
'pgadmin.feature_tests.keyboard_shortcut_test'] ]
# list of tests can be executed in parallel # list of tests can be executed in parallel
parallel_tests = list(module_list) parallel_tests = list(module_list)
@ -1704,3 +1705,28 @@ def create_user_wise_test_client(user):
return wrapper return wrapper
return multi_user_decorator 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

View File

@ -151,7 +151,7 @@ scenarios.apply_scenario = test_utils.apply_scenario
def get_suite(module_list, test_server, test_app_client, server_information, 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 This function add the tests to test suite and return modified test suite
variable. variable.
@ -164,6 +164,12 @@ def get_suite(module_list, test_server, test_app_client, server_information,
:type test_app_client: pgadmin app object :type test_app_client: pgadmin app object
:return pgadmin_suite: test suite with test cases :return pgadmin_suite: test suite with test cases
:rtype: TestSuite :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 = [] modules = []
pgadmin_suite = unittest.TestSuite() 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.setTestClient(test_app_client)
obj.setTestServer(test_server) obj.setTestServer(test_server)
obj.setDriver(driver_passed) obj.setDriver(driver_passed)
obj.setParallelUI_tests(parallel_ui_test)
obj.setServerInformation(server_information) obj.setServerInformation(server_information)
obj.setTestDatabaseName(test_db_name) obj.setTestDatabaseName(test_db_name)
scenario = scenarios.generate_scenarios(obj) scenario = scenarios.generate_scenarios(obj)
pgadmin_suite.addTests(scenario) pgadmin_suite.addTests(scenario)
return pgadmin_suite return pgadmin_suite
@ -450,12 +456,14 @@ class StreamToLogger(object):
pass 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 Function executes actually test
:param test_module_list_passed: :param test_module_list_passed: test modules
:param server_passed: :param server_passed: serve details
:param driver_passed: :param driver_passed: webdriver object
:param parallel_ui_test: parallel ui tests
:return: :return:
""" """
try: try:
@ -493,11 +501,17 @@ def execute_test(test_module_list_passed, server_passed, driver_passed):
test_utils.configure_preferences( test_utils.configure_preferences(
default_binary_path=server_passed['default_binary_paths']) 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 # Get unit test suit
suite = get_suite(test_module_list_passed, suite = get_suite(test_module_list_passed,
server_passed, server_passed,
test_client, 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 # Run unit test suit created
tests = unittest.TextTestRunner(stream=sys.stderr, 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: :param max_thread_count:
""" """
driver_object = None driver_object = None
try:
# Thread list # Thread list
threads_list = [] threads_list = []
# Create thread for each server # Create thread for each server
for ser in servers_details: for ser in servers_details:
try:
while True: while True:
# If active thread count <= max_thread_count, add new thread # If active thread count <= max_thread_count, add new thread
if threading.activeCount() <= max_thread_count: if threading.activeCount() <= max_thread_count:
@ -594,10 +607,10 @@ def run_parallel_tests(url_client, servers_details, parallel_tests_lists,
# Start thread # Start thread
t = threading.Thread(target=execute_test, name=thread_name, t = threading.Thread(target=execute_test, name=thread_name,
args=(parallel_tests_lists, ser, args=(parallel_tests_lists, ser,
driver_object)) driver_object, True))
threads_list.append(t) threads_list.append(t)
t.start() t.start()
time.sleep(3) time.sleep(10)
break break
# else sleep for 10 seconds # else sleep for 10 seconds
else: else:
@ -654,7 +667,7 @@ def run_sequential_tests(url_client, servers_details, sequential_tests_lists,
t = threading.Thread(target=execute_test, t = threading.Thread(target=execute_test,
name=thread_name, name=thread_name,
args=(sequential_tests_lists, ser, args=(sequential_tests_lists, ser,
driver_object)) driver_object, True))
t.start() t.start()
t.join() t.join()
except Exception as exc: except Exception as exc:
@ -779,25 +792,27 @@ if __name__ == '__main__':
if args['pkg'] is not None: if args['pkg'] is not None:
node_name = args['pkg'].split('.')[-1] 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 # Check if feature tests included & parallel tests switch passed
if test_utils.is_feature_test_included(args) and \ if test_utils.is_feature_test_included(args) and is_parallel_ui_tests:
test_utils.is_parallel_ui_tests(args): if config.SERVER_MODE:
try: try:
# Get selenium config dict # Get selenium config dict
selenoid_config = test_setup.config_data['selenoid_config'] selenoid_config = test_setup.config_data['selenoid_config']
# Set DEFAULT_SERVER value # Set DEFAULT_SERVER value
default_server = selenoid_config['pgAdmin_default_server'] default_server = selenoid_config['pgAdmin_default_server']
os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"] = str(default_server) os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"] = str(
default_server)
config.DEFAULT_SERVER = str(default_server) config.DEFAULT_SERVER = str(default_server)
# Get hub url # Get hub url
hub_url = selenoid_config['selenoid_url'] hub_url = selenoid_config['selenoid_url']
# Get selenium grid status & list of available browser out passed # Get selenium grid status & list of available browser
selenium_grid_status, list_of_browsers \ # out of passed
= test_utils.get_selenium_grid_status_and_browser_list(hub_url, selenium_grid_status, list_of_browsers = test_utils.\
args) get_selenium_grid_status_and_browser_list(hub_url, args)
# Execute tests if selenium-grid is up # Execute tests if selenium-grid is up
if selenium_grid_status and len(list_of_browsers) > 0: if selenium_grid_status and len(list_of_browsers) > 0:
@ -810,14 +825,16 @@ if __name__ == '__main__':
test_utils.get_browser_details(browser_info, test_utils.get_browser_details(browser_info,
hub_url) hub_url)
# test lists can be executed in parallel & sequentially # test lists can be executed in
# parallel & sequentially
parallel_tests, sequential_tests = \ parallel_tests, sequential_tests = \
test_utils.get_parallel_sequential_module_list( test_utils.get_parallel_sequential_module_list(
test_module_list) test_module_list)
# Print test summary # Print test summary
test_utils.print_test_summary( test_utils.print_test_summary(
test_module_list, parallel_tests, sequential_tests, test_module_list, parallel_tests,
sequential_tests,
browser_name, browser_version) browser_name, browser_version)
# Create app form source code # Create app form source code
@ -833,7 +850,8 @@ if __name__ == '__main__':
# Running Parallel tests # Running Parallel tests
if len(parallel_tests) > 0: if len(parallel_tests) > 0:
parallel_sessions = \ parallel_sessions = \
int(selenoid_config['max_parallel_sessions']) int(selenoid_config[
'max_parallel_sessions'])
run_parallel_tests( run_parallel_tests(
client_url, servers_info, parallel_tests, client_url, servers_info, parallel_tests,
@ -854,7 +872,6 @@ if __name__ == '__main__':
# not to mix output # not to mix output
time.sleep(5) time.sleep(5)
# Print note for completion of execution in a browser.
print( print(
"\n============= Test execution with {0} is " "\n============= Test execution with {0} is "
"completed.=============".format(browser_name), "completed.=============".format(browser_name),
@ -878,6 +895,11 @@ if __name__ == '__main__':
print(str(exc)) print(str(exc))
failure = True failure = True
del os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"] del os.environ["PGADMIN_CONFIG_DEFAULT_SERVER"]
else:
print(
"\n============= Please Turn on Server Mode to run selenoid "
"tests =============", file=sys.stderr)
failure = True
else: else:
try: try:
for server in servers_info: for server in servers_info: