From bbd97ef8a1377ca9ff18f8e48a11098d773de789 Mon Sep 17 00:00:00 2001 From: Atthaboon Sanurt Date: Sat, 16 Apr 2022 11:39:47 +0700 Subject: [PATCH 1/3] Fixed script freeze if test timeout occur --- Examples/utilities/timeout.robot | 28 +++++++++------ PuppeteerLibrary/__init__.py | 28 +++++++++------ .../keywords/browsermanagement.py | 34 ++++++++++++------- .../playwright/playwright_context.py | 7 ++-- 4 files changed, 61 insertions(+), 36 deletions(-) diff --git a/Examples/utilities/timeout.robot b/Examples/utilities/timeout.robot index e70c5e7..458a2a4 100644 --- a/Examples/utilities/timeout.robot +++ b/Examples/utilities/timeout.robot @@ -1,30 +1,36 @@ *** Settings *** -Library PuppeteerLibrary -Test Setup Open browser to test page -Test Teardown Close Browser -Suite Teardown Close Puppeteer +Library PuppeteerLibrary + +Suite Teardown Close Puppeteer +Test Setup Open browser to test page +Test Teardown Close All Browser *** Variables *** -${DEFAULT_BROWSER} chrome -${HOME_PAGE_URL} http://127.0.0.1:7272/basic-html-elements.html +${DEFAULT_BROWSER} chrome +${HOME_PAGE_URL} http://127.0.0.1:7272/basic-html-elements.html *** Test Cases *** Default timeout Set Timeout 3s Run Keyword And Expect Error No new page has been open.* Wait for new window open - + Timeout wait for new window open ${window count} = Get Window Count Run Async Keywords - ... Wait for new window open 5s AND + ... Wait for new window open 5s AND ... Click Element id:open-new-tab +# Test timeout will not freeze script +# [Documentation] Target to test test timeout not freeze the script. +# [Timeout] 5s +# Run Keyword And Expect Error * Wait Until Page Contains Element id:open-new-tabx 6s + + *** Keywords *** Open browser to test page - ${BROWSER} = Get variable value ${BROWSER} ${DEFAULT_BROWSER} + ${BROWSER} = Get variable value ${BROWSER} ${DEFAULT_BROWSER} ${HEADLESS} = Get variable value ${HEADLESS} ${False} - &{options} = create dictionary headless=${HEADLESS} + &{options} = create dictionary headless=${HEADLESS} Open browser ${HOME_PAGE_URL} browser=${BROWSER} options=${options} - diff --git a/PuppeteerLibrary/__init__.py b/PuppeteerLibrary/__init__.py index 7dc69ff..1bf8a24 100644 --- a/PuppeteerLibrary/__init__.py +++ b/PuppeteerLibrary/__init__.py @@ -1,9 +1,12 @@ import asyncio import os +import traceback import warnings import logging import signal import sys + +import robot from PuppeteerLibrary.keywords.checkbox import CheckboxKeywords from typing import List from PuppeteerLibrary.base.ipuppeteer_library import iPuppeteerLibrary @@ -113,7 +116,7 @@ class PuppeteerLibrary(DynamicCore, iPuppeteerLibrary): library_contexts: dict = {} def __init__(self, disable_python_logging=True): - + if disable_python_logging: self._disable_python_logging() @@ -151,7 +154,7 @@ def __init__(self, disable_python_logging=True): @not_keyword def get_current_library_context(self) -> iLibraryContext: return self.current_libary_context - + @not_keyword async def set_current_library_context(self, context_name) -> iLibraryContext: self.current_libary_context = self.library_contexts[context_name] @@ -180,8 +183,8 @@ def create_library_context(self, alias: str, browser_type: str) -> iLibraryConte library_context = self.library_factory.create(browser_type) self.library_contexts[alias] = library_context self.current_libary_context = library_context - return library_context - + return library_context + @not_keyword def remove_library_context(self, alias): if alias not in self.library_contexts.keys(): @@ -190,7 +193,8 @@ def remove_library_context(self, alias): del self.library_contexts[alias] if self.current_libary_context == deleted_library_context: if len(self.library_contexts) > 0: - self.current_libary_context = list(self.library_contexts.values())[-1] + self.current_libary_context = list( + self.library_contexts.values())[-1] else: self.current_libary_context = None @@ -199,8 +203,16 @@ def run_keyword(self, name, args, kwargs): self._running_keyword = name try: return DynamicCore.run_keyword(self, name, args, kwargs) + except robot.errors.TimeoutError: + logger.warn('Test timeout. Force stop puppeteer server') + # Force + self.loop.run_until_complete( + self.get_current_library_context().stop_server()) + raise except Exception: - if name.lower().replace(' ', '_') != 'capture_page_screenshot': + if name.lower().replace(' ', '_') == 'close_puppeteer' or name.lower().replace(' ', '_') == 'open_browser': + logger.warn('Can\'t close puppeteer...') + elif name.lower().replace(' ', '_') != 'capture_page_screenshot': self.failure_occurred() raise finally: @@ -229,7 +241,3 @@ def _disable_python_logging(self): def _stop_execution_gracefully(self): raise ExecutionFailed('Execution terminated by signal', exit=True) - -from ._version import get_versions -__version__ = get_versions()['version'] -del get_versions diff --git a/PuppeteerLibrary/keywords/browsermanagement.py b/PuppeteerLibrary/keywords/browsermanagement.py index 146f1b9..576ae18 100644 --- a/PuppeteerLibrary/keywords/browsermanagement.py +++ b/PuppeteerLibrary/keywords/browsermanagement.py @@ -1,5 +1,6 @@ import os import shutil +from robot.api import logger from PuppeteerLibrary.base.librarycomponent import LibraryComponent from PuppeteerLibrary.base.robotlibcore import keyword from PuppeteerLibrary.ikeywords.ibrowsermanagement_async import iBrowserManagementAsync @@ -49,12 +50,13 @@ def open_browser(self, url, browser="chrome", alias=None, options={}): """ if options is None: options = {} - + self.info(url) library_context = self.ctx.get_library_context_by_name(alias) if library_context is None: library_context = self.ctx.create_library_context(alias, browser) - self.loop.run_until_complete(self.ctx.set_current_library_context(alias)) + self.loop.run_until_complete( + self.ctx.set_current_library_context(alias)) self.loop.run_until_complete(library_context.start_server(options)) self.loop.run_until_complete(library_context.create_new_page(options)) self.loop.run_until_complete(self.get_async_keyword_group().go_to(url)) @@ -63,7 +65,8 @@ def open_browser(self, url, browser="chrome", alias=None, options={}): def close_window(self): """ Close current browser tab/page """ - self.loop.run_until_complete(self.ctx.get_current_library_context().close_window()) + self.loop.run_until_complete( + self.ctx.get_current_library_context().close_window()) @keyword def close_browser(self, alias=None): @@ -78,15 +81,17 @@ def close_browser(self, alias=None): def close_all_browser(self): """Close all browser """ - library_contexts = self.ctx.get_all_library_context() + library_contexts = self.ctx.get_all_library_context() for library_context in library_contexts: - self.loop.run_until_complete(library_context.close_browser_context()) + self.loop.run_until_complete( + library_context.close_browser_context()) @keyword def close_puppeteer(self): library_contexts_dict = self.ctx.get_all_library_context_dict() for key in list(library_contexts_dict.keys()): - self.loop.run_until_complete(library_contexts_dict[key].stop_server()) + self.loop.run_until_complete( + library_contexts_dict[key].stop_server()) self.ctx.remove_library_context(key) @keyword @@ -113,13 +118,14 @@ def go_to(self, url): @keyword def reload_page(self): """Reload the current page""" - self.loop.run_until_complete(self.get_async_keyword_group().reload_page()) + self.loop.run_until_complete( + self.get_async_keyword_group().reload_page()) @keyword def get_window_count(self): """ Get windows count """ - return self.loop.run_until_complete(self.get_async_keyword_group().get_window_count()) + return self.loop.run_until_complete(self.get_async_keyword_group().get_window_count()) @keyword def wait_for_new_window_open(self, timeout=None): @@ -131,7 +137,8 @@ def wait_for_new_window_open(self, timeout=None): | Run Async Keywords | Click Element | id:view_conditions | AND | | ... | `Wait For New Window Open` | | | """ - self.loop.run_until_complete(self.get_async_keyword_group().wait_for_new_window_open(timeout)) + self.loop.run_until_complete( + self.get_async_keyword_group().wait_for_new_window_open(timeout)) @keyword def switch_window(self, locator='MAIN'): @@ -142,7 +149,8 @@ def switch_window(self, locator='MAIN'): - title="QAHive": window title. Page title will have have error if new tab have auto redirection - url="https://qahive.com": url support regex Example: url=.*qahive.com """ - self.loop.run_until_complete(self.get_async_keyword_group().switch_window(locator)) + self.loop.run_until_complete( + self.get_async_keyword_group().switch_window(locator)) @keyword def switch_browser(self, alias): @@ -265,11 +273,12 @@ def delete_browser_storage_state(self, ref): *Limitation* only support Playwright browser """ - file_path = self.STATES_FOLDER +'/state-'+ ref + '.json' + file_path = self.STATES_FOLDER + '/state-' + ref + '.json' if os.path.exists(file_path): os.remove(file_path) else: - self.warn('Can not delete the storate '+ref+' as it doesn\'t exists') + self.warn('Can not delete the storate ' + + ref+' as it doesn\'t exists') @keyword def delete_all_browser_storage_states(self): @@ -281,4 +290,3 @@ def delete_all_browser_storage_states(self): shutil.rmtree(self.STATES_FOLDER) except OSError as e: self.warn("Error: %s - %s." % (e.filename, e.strerror)) - diff --git a/PuppeteerLibrary/playwright/playwright_context.py b/PuppeteerLibrary/playwright/playwright_context.py index a622b65..d48bf0b 100644 --- a/PuppeteerLibrary/playwright/playwright_context.py +++ b/PuppeteerLibrary/playwright/playwright_context.py @@ -1,4 +1,5 @@ import asyncio +from robot.api import logger from PuppeteerLibrary.custom_elements.base_page import BasePage from PuppeteerLibrary.playwright.custom_elements.playwright_page import PlaywrightPage from PuppeteerLibrary.playwright.async_keywords.playwright_checkbox import PlaywrightCheckbox @@ -75,7 +76,6 @@ async def start_server(self, options: dict = {}): del merged_options['proxy'] self.browser = await self.playwright.chromium.launch( headless=merged_options['headless'], proxy=proxy) - merged_options elif self.browser_type == "webkit": self.browser = await self.playwright.webkit.launch( headless=merged_options['headless']) @@ -85,7 +85,10 @@ async def start_server(self, options: dict = {}): self.browser.accept_downloads = True async def stop_server(self): - await self.playwright.stop() + try: + await asyncio.wait_for(self.playwright.stop(), timeout=3.0) + except Exception: + logger.warn('Can\'t stop server properly...') self._reset_server_context() def is_server_started(self) -> bool: From 0c96d90d28bcb2672ba55d3402641142d45262d3 Mon Sep 17 00:00:00 2001 From: Atthaboon Sanurt Date: Sat, 16 Apr 2022 11:41:23 +0700 Subject: [PATCH 2/3] Update close puppeteer timeout to 5 sec --- PuppeteerLibrary/playwright/playwright_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PuppeteerLibrary/playwright/playwright_context.py b/PuppeteerLibrary/playwright/playwright_context.py index d48bf0b..5667d19 100644 --- a/PuppeteerLibrary/playwright/playwright_context.py +++ b/PuppeteerLibrary/playwright/playwright_context.py @@ -86,7 +86,7 @@ async def start_server(self, options: dict = {}): async def stop_server(self): try: - await asyncio.wait_for(self.playwright.stop(), timeout=3.0) + await asyncio.wait_for(self.playwright.stop(), timeout=5.0) except Exception: logger.warn('Can\'t stop server properly...') self._reset_server_context() From a60a77bd53b93f2e8193ee2d2ed0e708f6b13a3d Mon Sep 17 00:00:00 2001 From: Atthaboon Sanurt Date: Sat, 16 Apr 2022 11:42:05 +0700 Subject: [PATCH 3/3] Update close browser to 5 sec --- PuppeteerLibrary/playwright/playwright_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PuppeteerLibrary/playwright/playwright_context.py b/PuppeteerLibrary/playwright/playwright_context.py index 5667d19..64c44f1 100644 --- a/PuppeteerLibrary/playwright/playwright_context.py +++ b/PuppeteerLibrary/playwright/playwright_context.py @@ -152,7 +152,7 @@ def get_browser_context(self): async def close_browser_context(self): if self.browser is not None: try: - await asyncio.wait_for(self.browser.close(), timeout=3) + await asyncio.wait_for(self.browser.close(), timeout=5) except asyncio.TimeoutError: None self._reset_context()