From 5185e0caaae7c4d3dffa05bf52d407c704b01231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Gonz=C3=A1lez=20Alonso?= Date: Fri, 31 May 2024 12:39:29 +0200 Subject: [PATCH] feat: stop playwright when driver is closed --- toolium/behave/environment.py | 16 ++++------ toolium/driver_wrapper.py | 53 +++++++++++++++++++++++++-------- toolium/driver_wrappers_pool.py | 2 +- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/toolium/behave/environment.py b/toolium/behave/environment.py index 8a35028f..11509469 100644 --- a/toolium/behave/environment.py +++ b/toolium/behave/environment.py @@ -156,6 +156,12 @@ def create_and_configure_wrapper(context): # Configure wrapper context.driver_wrapper.configure(context.config_files, behave_properties=behave_properties) + # Activate behave async context to execute playwright + if (context.driver_wrapper.config.get_optional('Driver', 'web_library') == 'playwright' + and context.driver_wrapper.async_loop is None): + use_or_create_async_context(context) + context.driver_wrapper.async_loop = context.async_context.loop + # Copy config object context.toolium_config = context.driver_wrapper.config @@ -227,12 +233,6 @@ def after_scenario(context, scenario): DriverWrappersPool.close_drivers(scope='function', test_name=scenario.name, test_passed=scenario.status in ['passed', 'skipped'], context=context) - # Stop playwright - if context.toolium_config.get_optional('Driver', 'web_library') == 'playwright' and hasattr(context, 'playwright'): - # TODO: reuse driver like in close_drivers - loop = context.async_context.loop - loop.run_until_complete(context.playwright.stop()) - # Save test status to be updated later if jira_test_status: add_jira_status(get_jira_key_from_scenario(scenario), jira_test_status, jira_test_comment) @@ -289,10 +289,6 @@ def start_driver(context, no_driver): :param context: behave context :param no_driver: True if this is an api test and driver should not be started """ - if context.toolium_config.get_optional('Driver', 'web_library') == 'playwright': - # Activate behave async context to execute playwright - use_or_create_async_context(context) - context.driver_wrapper.async_loop = context.async_context.loop create_and_configure_wrapper(context) if not no_driver: connect_wrapper(context) diff --git a/toolium/driver_wrapper.py b/toolium/driver_wrapper.py index b3ec879b..0d9e2470 100644 --- a/toolium/driver_wrapper.py +++ b/toolium/driver_wrapper.py @@ -56,6 +56,8 @@ class DriverWrapper(object): remote_node_video_enabled = False #: True if the remote grid node has the video recorder enabled logger = None #: logger instance async_loop = None #: async loop for playwright tests + playwright = None #: playwright instance + playwright_browser = None #: playwright browser instance # Configuration and output files config_properties_filenames = None #: configuration filenames separated by commas @@ -70,6 +72,9 @@ def __init__(self): default_wrapper = DriverWrappersPool.get_default_wrapper() self.config = default_wrapper.config.deepcopy() self.logger = default_wrapper.logger + self.async_loop = default_wrapper.async_loop + self.playwright = default_wrapper.playwright + self.playwright_browser = default_wrapper.playwright_browser self.config_properties_filenames = default_wrapper.config_properties_filenames self.config_log_filename = default_wrapper.config_log_filename self.output_log_filename = default_wrapper.output_log_filename @@ -204,7 +209,7 @@ def configure(self, tc_config_files, is_selenium_test=True, behave_properties=No self.configure_visual_baseline() def connect(self): - """Set up the selenium driver and connect to the server + """Set up the driver and connect to the server :returns: selenium or playwright driver """ @@ -212,10 +217,17 @@ def connect(self): return None if self.async_loop: - # Connect playwright driver - self.driver = self.connect_playwright(self.async_loop) - return self.driver + self.connect_playwright() + else: + self.connect_selenium() + return self.driver + + def connect_selenium(self): + """Set up selenium driver + + :returns: selenium driver + """ self.driver = ConfigDriver(self.config, self.utils).create_driver() # Save session id and remote node to download video after the test execution @@ -244,20 +256,37 @@ def connect(self): # Set implicitly wait timeout self.utils.set_implicitly_wait() - return self.driver - - def connect_playwright(self, async_loop): + def connect_playwright(self): """Set up the playwright page + It is a sync method because it is called from sync behave initialization method :returns: playwright page """ - # TODO: should playwright and browser be saved in driver_wrapper? - playwright = async_loop.run_until_complete(async_playwright().start()) + async_loop = self.async_loop + self.playwright = async_loop.run_until_complete(async_playwright().start()) # TODO: select browser from config headless_mode = self.config.getboolean_optional('Driver', 'headless') - browser = async_loop.run_until_complete(playwright.chromium.launch(headless=headless_mode)) - page = async_loop.run_until_complete(browser.new_page()) - return page + self.playwright_browser = async_loop.run_until_complete(self.playwright.chromium.launch(headless=headless_mode)) + self.driver = async_loop.run_until_complete(self.playwright_browser.new_page()) + + async def connect_playwright_new_page(self): + """Set up and additional playwright driver creating a new context and page in current browser instance + It is an async method to be called from async steps or page objects + + :returns: playwright driver + """ + context = await self.playwright_browser.new_context() + self.driver = await context.new_page() + return self.driver + + def stop(self): + """Stop selenium or playwright driver""" + if self.async_loop: + # Stop playwright driver + self.async_loop.run_until_complete(self.driver.close()) + else: + # Stop selenium driver + self.driver.quit() def resize_window(self): """Resize and move browser window""" diff --git a/toolium/driver_wrappers_pool.py b/toolium/driver_wrappers_pool.py index 7f2298b6..4aea05d7 100644 --- a/toolium/driver_wrappers_pool.py +++ b/toolium/driver_wrappers_pool.py @@ -167,7 +167,7 @@ def stop_drivers(cls, maintain_default=False): if not driver_wrapper.driver: continue try: - driver_wrapper.driver.quit() + driver_wrapper.stop() except Exception as e: driver_wrapper.logger.warning( "Capture exceptions to avoid errors in teardown method due to session timeouts: \n %s" % e)