From d1425a25cfad98c193c6af3048d74ed2ffe10e6e Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Fri, 28 Nov 2025 01:40:12 -0300 Subject: [PATCH 01/40] Add type hinting Add 'delay' paramter to server.dispose --- README.md | 9 +++------ caqui/asynchronous.py | 23 +++++++++++++++++------ caqui/easy/action_chains.py | 6 +++--- caqui/easy/capabilities.py | 27 +++++++++++++++++++-------- caqui/easy/page.py | 4 ++-- caqui/easy/server.py | 17 ++++++++++++----- caqui/synchronous.py | 3 ++- dev-requirements.txt | 3 ++- test-requirements.txt | 1 + tests/conftest.py | 13 +++++-------- tests/constants.py | 2 +- 11 files changed, 67 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 3bfee0d..8e4fe04 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Caqui - +[![Python application](https://github.com/douglasdcm/caqui/actions/workflows/python-app.yml/badge.svg)](https://github.com/douglasdcm/caqui/actions/workflows/python-app.yml) [![PyPI Downloads](https://static.pepy.tech/badge/caqui)](https://pepy.tech/projects/caqui) **Caqui** executes commands against Drivers synchronously and asynchronously. The intention is that the user does not worry about which Driver they're using. It can be **Web**Drivers like [Selenium](https://www.selenium.dev/), **Mobile**Drivers like [Appium](http://appium.io/docs/en/2.0/), or **Desktop**Drivers like [Winium](https://github.com/2gis/Winium.Desktop). It can also be used in remote calls. The user can start the Driver as a server in any host and provide the URL to **Caqui** clients. @@ -35,10 +35,8 @@ from tests.constants import PAGE_URL from caqui.easy import AsyncPage from caqui.by import By from caqui import synchronous -from caqui.easy.capabilities import ChromeOptionsBuilder from caqui.easy.options import ChromeOptionsBuilder from caqui.easy.server import Server -from time import sleep SERVER_PORT = 9999 SERVER_URL = f"http://localhost:{SERVER_PORT}" @@ -49,13 +47,12 @@ def setup_server(): server = Server.get_instance(port=SERVER_PORT) server.start() yield - sleep(3) - server.dispose() + server.dispose(delay=3) @fixture def setup_environment(): server_url = SERVER_URL - options = ChromeOptionsBuilder().args(["headless"]).to_dict() + options = ChromeOptionsBuilder().args(["headless"]) capabilities = ChromeCapabilitiesBuilder().accept_insecure_certs(True).add_options(options).to_dict() page = AsyncPage(server_url, capabilities, PAGE_URL) yield page diff --git a/caqui/asynchronous.py b/caqui/asynchronous.py index 7a13124..059c422 100644 --- a/caqui/asynchronous.py +++ b/caqui/asynchronous.py @@ -3,6 +3,7 @@ from caqui.constants import HEADERS as __HEADERS from caqui.exceptions import WebDriverError as WebDriverError from caqui import helper as __helper +from typing import Optional async def __handle_response(resp): @@ -32,7 +33,9 @@ async def __delete(url): async def __post(url, payload): try: async with __aiohttp.ClientSession() as session: - async with session.post(url, data=__json.dumps(payload), headers=__HEADERS) as resp: + async with session.post( + url, data=__json.dumps(payload), headers=__HEADERS + ) as resp: return await __handle_response(resp) except Exception as error: raise WebDriverError("'POST' request failed.") from error @@ -228,7 +231,9 @@ async def dismiss_alert(server_url, session): raise WebDriverError("Failed to dismiss alert.") from error -async def take_screenshot_element(server_url, session, element, path="/tmp", file_name="caqui"): +async def take_screenshot_element( + server_url, session, element, path="/tmp", file_name="caqui" +): """Take screenshot of element""" try: url = f"{server_url}/session/{session}/element/{element}/screenshot" @@ -444,7 +449,9 @@ async def set_timeouts(server_url, session, timeouts): raise WebDriverError("Failed to set timeouts.") from error -async def find_children_elements(server_url, session, parent_element, locator_type, locator_value): +async def find_children_elements( + server_url, session, parent_element, locator_type, locator_value +): """Find the children elements by 'locator_type' If the 'parent_element' is a shadow element, set the 'locator_type' as 'id' or @@ -461,7 +468,9 @@ async def find_children_elements(server_url, session, parent_element, locator_ty ) from error -async def find_child_element(server_url, session, parent_element, locator_type, locator_value): +async def find_child_element( + server_url, session, parent_element, locator_type, locator_value +): """Find the child element by 'locator_type'""" try: url = f"{server_url}/session/{session}/element/{parent_element}/element" @@ -773,7 +782,7 @@ async def find_element(server_url, session, locator_type, locator_value) -> dict ) from error -async def get_session(server_url: str, capabilities: dict = None) -> str: +async def get_session(server_url: str, capabilities: Optional[dict] = None) -> str: """ Opens a browser and a session. This session is used for all functions to perform events in the page @@ -785,4 +794,6 @@ async def get_session(server_url: str, capabilities: dict = None) -> str: response = await __post(url, capabilities) return response.get("sessionId") except Exception as error: - raise WebDriverError("Failed to open session. Check the browser capabilities.") from error + raise WebDriverError( + "Failed to open session. Check the browser capabilities." + ) from error diff --git a/caqui/easy/action_chains.py b/caqui/easy/action_chains.py index c9aa651..7a5a33f 100644 --- a/caqui/easy/action_chains.py +++ b/caqui/easy/action_chains.py @@ -1,12 +1,13 @@ from caqui import asynchronous from caqui.easy.element import Element +from typing import Coroutine class ActionChains: def __init__(self, driver) -> None: self.__remote = driver.remote self.__session = driver.session - self.__coroutines = [] + self.__coroutines: list[Coroutine] = [] def click(self, element: Element): """ @@ -37,6 +38,5 @@ def scroll_to_element(self, element: Element): async def perform(self): """Executes the chain of Coroutines""" - for coroutine in self.__coroutines: - await coroutine + [await coroutine for coroutine in self.__coroutines] return True diff --git a/caqui/easy/capabilities.py b/caqui/easy/capabilities.py index 3cdae8f..72ba3af 100644 --- a/caqui/easy/capabilities.py +++ b/caqui/easy/capabilities.py @@ -1,5 +1,7 @@ from math import ceil +from caqui.easy.options import BaseOptions + class Browser: """ @@ -19,7 +21,7 @@ class ProxyConfigurationBuilder: """ def __init__(self) -> None: - self.__proxy = {} + self.__proxy: dict = {} def proxy_type(self, proxy: str): """ @@ -127,7 +129,7 @@ class TimeoutsBuilder: """ def __init__(self) -> None: - self.__timeouts = {} + self.__timeouts: dict = {} def implicit(self, timeout: int): """Notice: if the number is a float, converts it to an integer""" @@ -164,8 +166,8 @@ class BaseCapabilities: """Reference: https://www.w3.org/TR/webdriver/#capabilities""" def __init__(self) -> None: - self.desired_capabilities = {} - self.options = {} + self.desired_capabilities: dict = {} + self.options: dict = {} def to_dict(self): raise NotImplementedError @@ -217,11 +219,13 @@ def page_load_strategy(self, strategy: str): } return self - def proxy(self, proxy_configuration: dict): + def proxy(self, proxy_configuration: dict | ProxyConfigurationBuilder): """ Defines the current session’s proxy configuration. Use the ProxyConfigurationBuilder class for simplicity. """ + if isinstance(proxy_configuration, ProxyConfigurationBuilder): + proxy_configuration = proxy_configuration.to_dict() self.desired_capabilities = { **self.desired_capabilities, **proxy_configuration, @@ -238,11 +242,13 @@ def set_window_rect(self, decison: bool): } return self - def timeouts(self, session_timeouts: dict): + def timeouts(self, session_timeouts: dict | TimeoutsBuilder): """ Describes the timeouts imposed on certain session operations. Use the TimeoutsBuilder class for simplicity. """ + if isinstance(session_timeouts, TimeoutsBuilder): + session_timeouts = session_timeouts.to_dict() self.desired_capabilities = { **self.desired_capabilities, **session_timeouts, @@ -293,11 +299,13 @@ def user_agent(self, agent: str): } return self - def add_options(self, options: dict): + def add_options(self, options: dict | BaseOptions): """Add vendor options, for example {"goog:chromeOptions": {"extensions": [], "args": ["--headless"]}} or {"moz:experimental-webdriver": true} """ + if isinstance(options, BaseOptions): + options = options.to_dict() self.options = options return self @@ -329,5 +337,8 @@ def to_dict(self): """ result = {"capabilities": self.desired_capabilities} if self.options: - result["capabilities"] = {**result["capabilities"], **{"firstMatch": self.options}} + result["capabilities"] = { + **result["capabilities"], + **{"firstMatch": self.options}, + } return result diff --git a/caqui/easy/page.py b/caqui/easy/page.py index 8137f7d..bfaadc6 100644 --- a/caqui/easy/page.py +++ b/caqui/easy/page.py @@ -1,5 +1,5 @@ import os -from typing import Union +from typing import Optional, Union from caqui import asynchronous, synchronous from caqui.easy.action_chains import ActionChains from caqui.easy.window import Window @@ -11,7 +11,7 @@ class AsyncPage: def __init__( - self, server_url: str, capabilities: dict = None, url: Union[str, None] = None + self, server_url: str, capabilities: Optional[dict] = None, url: Union[str, None] = None ) -> None: """Mimics Selenium methods""" if not capabilities: diff --git a/caqui/easy/server.py b/caqui/easy/server.py index 2d4bdc1..b00bc64 100644 --- a/caqui/easy/server.py +++ b/caqui/easy/server.py @@ -1,14 +1,15 @@ +import requests +import subprocess from time import sleep from typing import Union from requests import head from requests.exceptions import ConnectionError -import requests -import subprocess from webdriver_manager.core.manager import DriverManager from webdriver_manager.chrome import ChromeDriverManager from caqui.exceptions import ServerError -TIMEOUT = 120 # seconds +TIMEOUT = 120 # seconds + class Server: """ @@ -42,7 +43,7 @@ def __wait_server(self): requests.get(self.url, timeout=TIMEOUT) break except ConnectionError: - sleep(1) + sleep(0.5) if i == (MAX_RETIES - 1): self.__process.kill() self.__process.wait() @@ -88,10 +89,16 @@ def process(self): """Returns the process (PID)""" return self.__process - def dispose(self): + def dispose(self, delay: float = 0): """ Disposes the driver process. + + Args: + delay: Delay execution for a given number of seconds. + The argument may be a floating point number for subsecond precision. """ + if delay: + sleep(delay) if self.__process: self.__process.kill() self.__process.wait() diff --git a/caqui/synchronous.py b/caqui/synchronous.py index f3bc5fa..c5ee515 100644 --- a/caqui/synchronous.py +++ b/caqui/synchronous.py @@ -3,6 +3,7 @@ from caqui.exceptions import WebDriverError as WebDriverError from caqui import helper as __helper from caqui.constants import HEADERS as __HEADERS +from typing import Optional def __handle_response(response): @@ -740,7 +741,7 @@ def __get_session(response) -> str: return response.get("sessionId") -def get_session(server_url: str, capabilities: dict = None): +def get_session(server_url: str, capabilities: Optional[dict] = None): """ Opens a browser and a session. This session is used for all functions to perform events in the page diff --git a/dev-requirements.txt b/dev-requirements.txt index 91f6e12..0267580 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,4 @@ requests aiohttp -webdriver_manager \ No newline at end of file +webdriver_manager +types-requests \ No newline at end of file diff --git a/test-requirements.txt b/test-requirements.txt index ad1abef..ec3406d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,3 +5,4 @@ coverage tox pytest-asyncio build +mypy diff --git a/tests/conftest.py b/tests/conftest.py index aad06bb..61e1444 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -from time import sleep from pytest import fixture from tests.constants import PAGE_URL from caqui.easy import AsyncPage @@ -13,7 +12,7 @@ def __build_capabilities(): - options = ChromeOptionsBuilder().args(["headless"]).to_dict() + options = ChromeOptionsBuilder().args(["headless"]) capabilities = ( ChromeCapabilitiesBuilder() .accept_insecure_certs(True) @@ -28,8 +27,7 @@ def setup_server(): server = Server.get_instance(port=SERVER_PORT) server.start() yield - sleep(3) - server.dispose() + server.dispose(delay=3) @fixture @@ -47,10 +45,8 @@ def setup_functional_environment(): synchronous.dismiss_alert(server_url, session) except Exception: pass - try: + finally: synchronous.close_session(server_url, session) - except Exception: - pass @fixture @@ -63,4 +59,5 @@ def setup_environment(): synchronous.dismiss_alert(server_url, page.session) except Exception: pass - page.quit() + finally: + page.quit() diff --git a/tests/constants.py b/tests/constants.py index 01d3430..dfbc00c 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -13,4 +13,4 @@ "sameSite": "Lax", "secure": True, "value": "523=Sc0_gsThISC9jkAfuOsEdaX51SxT6FWqrG3UWhn7eaw5JZooxNWC2jbQZVadDFgM4OYLjDSTAYPb3rQdKt23GQgDcTa_iuLSOyJ7Tlpo3PKa_ijrjrcoMeIWT6O6DnvvG1q8tSfeahhzv44f9cgkJrjZ5VPC4wg1ZKocrQFiJOZEIS6XZpsK73d2hnw0HZkTymQsYt3UVoWrsqPujsTzw542M45aSRl3U406lNMU9zailbJurvW6ZRVL2TIaaUMhkQ", # noqa E501 -} \ No newline at end of file +} From d49b40a0e4b6714a096bdac9994cc7988bd6a377 Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:55:17 -0300 Subject: [PATCH 02/40] add single async session option --- caqui/asynchronous.py | 684 +++++---- caqui/easy/action_chains.py | 28 +- caqui/easy/alert.py | 13 +- caqui/easy/element.py | 61 +- caqui/easy/page.py | 79 +- caqui/easy/switch_to.py | 35 +- tests/conftest.py | 24 +- tests/feature/test_async_with_http_session.py | 1245 +++++++++++++++++ tests/feature/test_sync_and_async.py | 160 ++- tests/integration/test_async_scenarios.py | 10 +- tests/unit/test_async_unit.py | 62 +- 11 files changed, 1938 insertions(+), 463 deletions(-) create mode 100644 tests/feature/test_async_with_http_session.py diff --git a/caqui/asynchronous.py b/caqui/asynchronous.py index 059c422..05f29f0 100644 --- a/caqui/asynchronous.py +++ b/caqui/asynchronous.py @@ -1,12 +1,12 @@ -import aiohttp as __aiohttp -import json as __json +from aiohttp import ClientSession +from json import dumps from caqui.constants import HEADERS as __HEADERS from caqui.exceptions import WebDriverError as WebDriverError -from caqui import helper as __helper +from caqui.helper import save_picture, get_elements, get_element from typing import Optional -async def __handle_response(resp): +async def _handle_response(resp): result = None if resp.status in range(200, 399): result = await resp.json() @@ -21,141 +21,179 @@ async def __handle_response(resp): return result -async def __delete(url): - try: - async with __aiohttp.ClientSession() as session: - async with session.delete(url, headers=__HEADERS) as resp: - return await __handle_response(resp) - except Exception as error: - raise WebDriverError("'DELETE' request failed.") from error - +async def _delete(url, session_http: ClientSession = None): + if session_http: + try: + async with session_http.delete(url, headers=__HEADERS) as resp: + return await _handle_response(resp) + except Exception as e: + raise WebDriverError("'DELETE' request failed.") from e -async def __post(url, payload): - try: - async with __aiohttp.ClientSession() as session: - async with session.post( - url, data=__json.dumps(payload), headers=__HEADERS + else: + try: + async with ClientSession() as session_http: + async with session_http.delete(url, headers=__HEADERS) as resp: + return await _handle_response(resp) + except Exception as e: + raise WebDriverError("'DELETE' request failed.") from e + + +async def _post(url, payload, session_http: ClientSession = None): + if session_http: + try: + async with session_http.post( + url, data=dumps(payload), headers=__HEADERS ) as resp: - return await __handle_response(resp) - except Exception as error: - raise WebDriverError("'POST' request failed.") from error - - -async def __get(url): - try: - async with __aiohttp.ClientSession() as session: - async with session.get(url, headers=__HEADERS) as resp: - return await __handle_response(resp) - except Exception as error: - raise WebDriverError("'GET' request failed.") from error + return await _handle_response(resp) + except Exception as e: + raise WebDriverError("'POST' request failed.") from e + else: + try: + async with ClientSession() as session_http: + async with session_http.post( + url, data=dumps(payload), headers=__HEADERS + ) as resp: + return await _handle_response(resp) + except Exception as e: + raise WebDriverError("'POST' request failed.") from e + + +async def _get(url, session_http: ClientSession = None): + if session_http: + try: + async with session_http.get(url, headers=__HEADERS) as resp: + return await _handle_response(resp) + except Exception as e: + raise WebDriverError("'GET' request failed.") from e + else: + try: + async with ClientSession() as session_http: + async with session_http.get(url, headers=__HEADERS) as resp: + return await _handle_response(resp) + except Exception as e: + raise WebDriverError("'GET' request failed.") from e -async def __handle_alert(server_url, session, command) -> bool: +async def _handle_alert(server_url, session, command, session_http) -> bool: url = f"{server_url}/session/{session}/alert/{command}" payload = { "value": command, } - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True -async def __handle_window(server_url, session, command): +async def _handle_window( + server_url, session, command, session_http: ClientSession = None +): url = f"{server_url}/session/{session}/window/{command}" payload = {} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True -async def add_cookie(server_url, session, cookie): +async def add_cookie(server_url, session, cookie, session_http: ClientSession = None): """Add cookie""" try: url = f"{server_url}/session/{session}/cookie" payload = {"cookie": cookie} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to add cookie.") from error + except Exception as e: + raise WebDriverError("Failed to add cookie.") from e -async def delete_cookie(server_url, session, name): +async def delete_cookie(server_url, session, name, session_http: ClientSession = None): """Delete cookie by name""" try: url = f"{server_url}/session/{session}/cookie/{name}" - await __delete(url) + await _delete(url, session_http) return True - except Exception as error: - raise WebDriverError(f"Failed to delete cookie '{name}'.") from error + except Exception as e: + raise WebDriverError(f"Failed to delete cookie '{name}'.") from e -async def refresh_page(server_url, session): +async def refresh_page(server_url, session, session_http: ClientSession = None): """Refresh page""" try: url = f"{server_url}/session/{session}/refresh" payload = {} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to refresh page.") from error + except Exception as e: + raise WebDriverError("Failed to refresh page.") from e -async def go_forward(server_url, session): +async def go_forward(server_url, session, session_http: ClientSession = None): """Go to page forward""" try: url = f"{server_url}/session/{session}/forward" payload = {} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to go to page forward.") from error + except Exception as e: + raise WebDriverError("Failed to go to page forward.") from e -async def set_window_rectangle(server_url, session, width, height, x, y): +async def set_window_rectangle( + server_url, session, width, height, x, y, session_http=None +): """Set window rectangle""" try: url = f"{server_url}/session/{session}/window/rect" payload = {"width": width, "height": height, "x": x, "y": y} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to set window rectangle.") from error + except Exception as e: + raise WebDriverError("Failed to set window rectangle.") from e -async def fullscreen_window(server_url, session): +async def fullscreen_window(server_url, session, session_http: ClientSession = None): """Fullscreen window""" try: - return await __handle_window(server_url, session, command="fullscreen") - except Exception as error: - raise WebDriverError("Failed to fullscreen window.") from error + return await _handle_window( + server_url, session, command="fullscreen", session_http=session_http + ) + except Exception as e: + raise WebDriverError("Failed to fullscreen window.") from e -async def minimize_window(server_url, session): +async def minimize_window(server_url, session, session_http: ClientSession = None): """Minimize window""" try: - return await __handle_window(server_url, session, command="minimize") - except Exception as error: - raise WebDriverError("Failed to minimize window.") from error + return await _handle_window( + server_url, session, command="minimize", session_http=session_http + ) + except Exception as e: + raise WebDriverError("Failed to minimize window.") from e -async def maximize_window(server_url, session): +async def maximize_window(server_url, session, session_http: ClientSession = None): """Maximize window""" try: - return await __handle_window(server_url, session, command="maximize") - except Exception as error: - raise WebDriverError("Failed to maximize window.") from error + return await _handle_window( + server_url, session, command="maximize", session_http=session_http + ) + except Exception as e: + raise WebDriverError("Failed to maximize window.") from e -async def switch_to_window(server_url, session, handle): +async def switch_to_window( + server_url, session, handle, session_http: ClientSession = None +): """Switch to window""" try: url = f"{server_url}/session/{session}/window" payload = {"name": handle} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to switch to window.") from error + except Exception as e: + raise WebDriverError("Failed to switch to window.") from e -async def new_window(server_url, session, window_type="tab") -> str: +async def new_window( + server_url, session, window_type="tab", session_http: ClientSession = None +) -> str: """Open a new window :param window_type (str): tab or window @@ -164,167 +202,202 @@ async def new_window(server_url, session, window_type="tab") -> str: try: url = f"{server_url}/session/{session}/window/new" payload = {"type": window_type} - result = await __post(url, payload) + result = await _post(url, payload, session_http=session_http) return result.get("value", {}).get("handle") - except Exception as error: - raise WebDriverError("Failed to open window.") from error + except Exception as e: + raise WebDriverError("Failed to open window.") from e -async def switch_to_parent_frame(server_url, session, element_frame): +async def switch_to_parent_frame( + server_url, session, element_frame, session_http: ClientSession = None +): """Switch to parent frame of 'element_frame'""" try: url = f"{server_url}/session/{session}/frame/parent" payload = {"id": {"ELEMENT": element_frame}} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to switch to parent frame.") from error + except Exception as e: + raise WebDriverError("Failed to switch to parent frame.") from e -async def switch_to_frame(server_url, session, element_frame): +async def switch_to_frame( + server_url, session, element_frame, session_http: ClientSession = None +): """Switch to frame 'element_frame'""" try: url = f"{server_url}/session/{session}/frame" payload = {"id": {"ELEMENT": element_frame}} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to switch to frame.") from error + except Exception as e: + raise WebDriverError("Failed to switch to frame.") from e -async def delete_all_cookies(server_url, session): +async def delete_all_cookies(server_url, session, session_http: ClientSession = None): """Delete all cookies""" try: url = f"{server_url}/session/{session}/cookie" - await __delete(url) + await _delete(url, session_http) return True - except Exception as error: - raise WebDriverError("Failed to delete cookies.") from error + except Exception as e: + raise WebDriverError("Failed to delete cookies.") from e -async def send_alert_text(server_url, session, text): +async def send_alert_text( + server_url, session, text, session_http: ClientSession = None +): """Fill the alert text area and send the text""" try: url = f"{server_url}/session/{session}/alert/text" payload = { "text": text, } - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to sent text to alert.") from error + except Exception as e: + raise WebDriverError("Failed to sent text to alert.") from e -async def accept_alert(server_url, session): +async def accept_alert(server_url, session, session_http: ClientSession = None): """Accept alert""" try: - return await __handle_alert(server_url, session, "accept") - except Exception as error: - raise WebDriverError("Failed to accept alert.") from error + return await _handle_alert( + server_url, session, "accept", session_http=session_http + ) + except Exception as e: + raise WebDriverError("Failed to accept alert.") from e -async def dismiss_alert(server_url, session): +async def dismiss_alert(server_url, session, session_http: ClientSession = None): """Dismiss alert""" try: - return await __handle_alert(server_url, session, "dismiss") - except Exception as error: - raise WebDriverError("Failed to dismiss alert.") from error + return await _handle_alert( + server_url, session, "dismiss", session_http=session_http + ) + except Exception as e: + raise WebDriverError("Failed to dismiss alert.") from e async def take_screenshot_element( - server_url, session, element, path="/tmp", file_name="caqui" + server_url, + session, + element, + path="/tmp", + file_name="caqui", + session_http: ClientSession = None, ): """Take screenshot of element""" try: url = f"{server_url}/session/{session}/element/{element}/screenshot" - response = await __get(url) + response = await _get(url, session_http) picture = response.get("value") - __helper.save_picture(session, path, file_name, picture) + save_picture(session, path, file_name, picture) return True - except Exception as error: - raise WebDriverError("Failed to take screenshot from element.") from error + except Exception as e: + raise WebDriverError("Failed to take screenshot from element.") from e -async def take_screenshot(server_url, session, path="/tmp", file_name="caqui"): +async def take_screenshot( + server_url, + session, + path="/tmp", + file_name="caqui", + session_http: ClientSession = None, +): """Take screenshot""" try: url = f"{server_url}/session/{session}/screenshot" - response = await __get(url) + response = await _get(url, session_http) picture = response.get("value") - __helper.save_picture(session, path, file_name, picture) + save_picture(session, path, file_name, picture) return True - except Exception as error: - raise WebDriverError("Failed to take screenshot.") from error + except Exception as e: + raise WebDriverError("Failed to take screenshot.") from e -async def get_named_cookie(server_url, session, name) -> str: +async def get_named_cookie( + server_url, session, name, session_http: ClientSession = None +) -> str: """Get cookie by name""" try: url = f"{server_url}/session/{session}/cookie/{name}" - response = await __get(url) + response = await _get(url, session_http) return response.get("value") - except Exception as error: - raise WebDriverError(f"Failed to get cookie '{name}'.") from error + except Exception as e: + raise WebDriverError(f"Failed to get cookie '{name}'.") from e -async def get_computed_label(server_url, session, element) -> str: +async def get_computed_label( + server_url, session, element, session_http: ClientSession = None +) -> str: """Get the element tag computed label. Get the accessibility name""" try: url = f"{server_url}/session/{session}/element/{element}/computedlabel" - response = await __get(url) + response = await _get(url, session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get element computed label.") from error + except Exception as e: + raise WebDriverError("Failed to get element computed label.") from e -async def get_computed_role(server_url, session, element) -> str: +async def get_computed_role( + server_url, session, element, session_http: ClientSession = None +) -> str: """Get the element tag computed role (the element role)""" try: url = f"{server_url}/session/{session}/element/{element}/computedrole" - response = await __get(url) + response = await _get(url, session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get element computed role.") from error + except Exception as e: + raise WebDriverError("Failed to get element computed role.") from e -async def get_tag_name(server_url, session, element) -> str: +async def get_tag_name( + server_url, session, element, session_http: ClientSession = None +) -> str: """Get the element tag name""" try: url = f"{server_url}/session/{session}/element/{element}/name" - response = await __get(url) + response = await _get(url, session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get element name.") from error + except Exception as e: + raise WebDriverError("Failed to get element name.") from e -async def get_shadow_root(server_url, session, element) -> dict: +async def get_shadow_root( + server_url, session, element, session_http: ClientSession = None +) -> dict: """Get the shadow root element""" try: root_element = "shadow-6066-11e4-a52e-4f735466cecf" url = f"{server_url}/session/{session}/element/{element}/shadow" - response = await __get(url) + response = await _get(url, session_http) return response.get("value", {}).get(root_element) - except Exception as error: - raise WebDriverError("Failed to get element shadow.") from error + except Exception as e: + raise WebDriverError("Failed to get element shadow.") from e -async def get_rect(server_url, session, element) -> dict: +async def get_rect( + server_url, session, element, session_http: ClientSession = None +) -> dict: """Get the element rectangle""" try: url = f"{server_url}/session/{session}/element/{element}/rect" - response = await __get(url) + response = await _get(url, session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get element rect.") from error + except Exception as e: + raise WebDriverError("Failed to get element rect.") from e -async def actions(server_url, session, payload): +async def actions(server_url, session, payload, session_http: ClientSession = None): url = f"{server_url}/session/{session}/actions" - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True -async def actions_move_to_element(server_url, session, element): +async def actions_move_to_element( + server_url, session, element, session_http: ClientSession = None +): """Move to an element simulating a mouse movement""" try: payload = { @@ -350,12 +423,14 @@ async def actions_move_to_element(server_url, session, element): }, ] } - return await actions(server_url, session, payload) - except Exception as error: - raise WebDriverError("Failed to move to element.") from error + return await actions(server_url, session, payload, session_http=session_http) + except Exception as e: + raise WebDriverError("Failed to move to element.") from e -async def actions_scroll_to_element(server_url, session, element): +async def actions_scroll_to_element( + server_url, session, element, session_http: ClientSession = None +): """Scroll to an element simulating a mouse movement""" try: payload = { @@ -377,12 +452,12 @@ async def actions_scroll_to_element(server_url, session, element): } ] } - return await actions(server_url, session, payload) - except Exception as error: - raise WebDriverError("Failed to scroll to element.") from error + return await actions(server_url, session, payload, session_http=session_http) + except Exception as e: + raise WebDriverError("Failed to scroll to element.") from e -async def submit(server_url, session, element): +async def submit(server_url, session, element, session_http: ClientSession = None): """Submit a form. It is similar to 'submit' funtion in Seleniu It is not part of W3C WebDriver. Just added for convenience """ @@ -393,13 +468,18 @@ async def submit(server_url, session, element): element, locator_type="xpath", locator_value="*[@type='submit']", + session_http=session_http + ) + return await click( + server_url, session, submit_element, session_http=session_http ) - return await click(server_url, session, submit_element) - except Exception as error: - raise WebDriverError("Failed to submit form.") from error + except Exception as e: + raise WebDriverError("Failed to submit form.") from e -async def actions_click(server_url, session, element): +async def actions_click( + server_url, session, element, session_http: ClientSession = None +): """Click an element simulating a mouse movement""" try: payload = { @@ -431,26 +511,33 @@ async def actions_click(server_url, session, element): }, ] } - return await actions(server_url, session, payload) - except Exception as error: - raise WebDriverError("Failed to click the element.") from error + return await actions(server_url, session, payload, session_http=session_http) + except Exception as e: + raise WebDriverError("Failed to click the element.") from e -async def set_timeouts(server_url, session, timeouts): +async def set_timeouts( + server_url, session, timeouts, session_http: ClientSession = None +): """Set timeouts""" try: url = f"{server_url}/session/{session}/timeouts" payload = { "implicit": timeouts, } - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to set timeouts.") from error + except Exception as e: + raise WebDriverError("Failed to set timeouts.") from e async def find_children_elements( - server_url, session, parent_element, locator_type, locator_value + server_url, + session, + parent_element, + locator_type, + locator_value, + session_http: ClientSession = None, ): """Find the children elements by 'locator_type' @@ -460,152 +547,175 @@ async def find_children_elements( try: url = f"{server_url}/session/{session}/element/{parent_element}/elements" payload = {"using": locator_type, "value": locator_value, "id": parent_element} - response = await __post(url, payload) - return __helper.get_elements(response) - except Exception as error: + response = await _post(url, payload, session_http=session_http) + return get_elements(response) + except Exception as e: raise WebDriverError( f"Failed to find the children elements from '{parent_element}'." - ) from error + ) from e async def find_child_element( - server_url, session, parent_element, locator_type, locator_value + server_url, + session, + parent_element, + locator_type, + locator_value, + session_http: ClientSession = None, ): """Find the child element by 'locator_type'""" try: url = f"{server_url}/session/{session}/element/{parent_element}/element" payload = {"using": locator_type, "value": locator_value, "id": parent_element} - response = await __post(url, payload) - return __helper.get_element(response) - except Exception as error: + response = await _post(url, payload, session_http=session_http) + return get_element(response) + except Exception as e: raise WebDriverError( f"Failed to find the child element from '{parent_element}'." - ) from error + ) from e -async def get_page_source(server_url, session) -> str: +async def get_page_source( + server_url, session, session_http: ClientSession = None +) -> str: """Get the page source (all content)""" try: url = f"{server_url}/session/{session}/source" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get the page source.") from error + except Exception as e: + raise WebDriverError("Failed to get the page source.") from e -async def execute_script(server_url, session, script, args=[]): +async def execute_script( + server_url, session, script, args=[], session_http: ClientSession = None +): """Executes a script, like 'alert('something')' to open an alert window""" try: url = f"{server_url}/session/{session}/execute/sync" payload = {"script": script, "args": args} - response = await __post(url, payload) + response = await _post(url, payload, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to execute script.") from error + except Exception as e: + raise WebDriverError("Failed to execute script.") from e -async def get_alert_text(server_url, session) -> str: +async def get_alert_text( + server_url, session, session_http: ClientSession = None +) -> str: """Get the text from an alert""" try: url = f"{server_url}/session/{session}/alert/text" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get the alert text.") from error + except Exception as e: + raise WebDriverError("Failed to get the alert text.") from e -async def get_active_element(server_url, session): +async def get_active_element(server_url, session, session_http: ClientSession = None): """Get the active element""" try: url = f"{server_url}/session/{session}/element/active" - response = await __get(url) - return __helper.get_element(response) - except Exception as error: - raise WebDriverError("Failed to check if element is selected.") from error + response = await _get(url, session_http=session_http) + return get_element(response) + except Exception as e: + raise WebDriverError("Failed to check if element is selected.") from e -async def clear_element(server_url, session, element): +async def clear_element( + server_url, session, element, session_http: ClientSession = None +): """Clear the element text""" try: url = f"{server_url}/session/{session}/element/{element}/clear" payload = {"id": element} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to clear the element text.") from error + except Exception as e: + raise WebDriverError("Failed to clear the element text.") from e -async def is_element_enabled(server_url, session, element) -> bool: +async def is_element_enabled( + server_url, session, element, session_http: ClientSession = None +) -> bool: """Check if element is enabled""" try: url = f"{server_url}/session/{session}/element/{element}/enabled" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to check if element is enabled.") from error + except Exception as e: + raise WebDriverError("Failed to check if element is enabled.") from e -async def get_css_value(server_url, session, element, property_name) -> str: +async def get_css_value( + server_url, session, element, property_name, session_http: ClientSession = None +) -> str: """Check if element is selected""" try: url = f"{server_url}/session/{session}/element/{element}/css/{property_name}" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to check if element is selected.") from error + except Exception as e: + raise WebDriverError("Failed to check if element is selected.") from e -async def is_element_selected(server_url, session, element) -> str: +async def is_element_selected( + server_url, session, element, session_http: ClientSession = None +) -> str: """Check if element is selected""" try: url = f"{server_url}/session/{session}/element/{element}/selected" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to check if element is selected.") from error + except Exception as e: + raise WebDriverError("Failed to check if element is selected.") from e -async def get_window_rectangle(server_url, session) -> dict: +async def get_window_rectangle( + server_url, session, session_http: ClientSession = None +) -> dict: """Get window rectangle""" try: url = f"{server_url}/session/{session}/window/rect" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get window rectangle.") from error + except Exception as e: + raise WebDriverError("Failed to get window rectangle.") from e -async def get_window_handles(server_url, session) -> list: +async def get_window_handles( + server_url, session, session_http: ClientSession = None +) -> list: """Get window handles""" try: url = f"{server_url}/session/{session}/window/handles" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get window handles.") from error + except Exception as e: + raise WebDriverError("Failed to get window handles.") from e -async def close_window(server_url, session) -> list: +async def close_window(server_url, session, session_http: ClientSession = None) -> list: """Close active window""" try: url = f"{server_url}/session/{session}/window" - response = await __delete(url) + response = await _delete(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to close active window.") from error + except Exception as e: + raise WebDriverError("Failed to close active window.") from e -async def get_window(server_url, session) -> str: +async def get_window(server_url, session, session_http: ClientSession = None) -> str: """Get window""" try: url = f"{server_url}/session/{session}/window" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get window.") from error + except Exception as e: + raise WebDriverError("Failed to get window.") from e -async def go_back(server_url, session): +async def go_back(server_url, session, session_http: ClientSession = None): """ This command causes the browser to traverse one step backward in the joint session history of the @@ -613,176 +723,192 @@ async def go_back(server_url, session): """ try: url = f"{server_url}/session/{session}/back" - await __post(url, {}) + await _post(url, {}, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to go back to page.") from error + except Exception as e: + raise WebDriverError("Failed to go back to page.") from e -async def get_url(server_url, session) -> str: +async def get_url(server_url, session, session_http: ClientSession = None) -> str: """Returns the URL from web page:""" try: url = f"{server_url}/session/{session}/url" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get page url.") from error + except Exception as e: + raise WebDriverError("Failed to get page url.") from e -async def get_timeouts(server_url, session) -> dict: +async def get_timeouts(server_url, session, session_http: ClientSession = None) -> dict: """ Returns the configured timeouts: {"implicit": 0, "pageLoad": 300000, "script": 30000} """ try: url = f"{server_url}/session/{session}/timeouts" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get timeouts.") from error + except Exception as e: + raise WebDriverError("Failed to get timeouts.") from e -async def get_status(server_url) -> dict: +async def get_status(server_url, session_http: ClientSession = None) -> dict: """Returns the status and details of the WebDriver""" try: url = f"{server_url}/status" - response = await __get(url) + response = await _get(url, session_http=session_http) return response - except Exception as error: - raise WebDriverError("Failed to get status.") from error + except Exception as e: + raise WebDriverError("Failed to get status.") from e -async def get_title(server_url, session) -> str: +async def get_title(server_url, session, session_http: ClientSession = None) -> str: """Get the page title""" try: url = f"{server_url}/session/{session}/title" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get page title.") from error + except Exception as e: + raise WebDriverError("Failed to get page title.") from e -async def find_elements(server_url, session, locator_type, locator_value) -> str: +async def find_elements( + server_url, session, locator_type, locator_value, session_http: ClientSession = None +) -> str: """Search the DOM elements by 'locator', for example, 'xpath'""" try: payload = {"using": locator_type, "value": locator_value} url = f"{server_url}/session/{session}/elements" - response = await __post(url, payload) + response = await _post(url, payload, session_http=session_http) return [x.get("ELEMENT") for x in response.get("value")] - except Exception as error: + except Exception as e: raise WebDriverError( f"Failed to find element by '{locator_type}'-'{locator_value}'." - ) from error + ) from e -async def get_property(server_url, session, element, property) -> str: +async def get_property( + server_url, session, element, property, session_http: ClientSession = None +) -> str: """Get the given HTML property of an element, for example, 'href'""" try: url = f"{server_url}/session/{session}/element/{element}/property/{property}" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get value from element.") from error + except Exception as e: + raise WebDriverError("Failed to get value from element.") from e -async def get_attribute(server_url, session, element, attribute) -> str: +async def get_attribute( + server_url, session, element, attribute, session_http: ClientSession = None +) -> str: """Get the given HTML attribute of an element, for example, 'aria-valuenow'""" try: url = f"{server_url}/session/{session}/element/{element}/attribute/{attribute}" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get value from element.") from error + except Exception as e: + raise WebDriverError("Failed to get value from element.") from e -async def get_text(server_url, session, element) -> str: +async def get_text( + server_url, session, element, session_http: ClientSession = None +) -> str: """Get the text of an element""" try: url = f"{server_url}/session/{session}/element/{element}/text" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get text from element.") from error + except Exception as e: + raise WebDriverError("Failed to get text from element.") from e -async def get_cookies(server_url, session) -> list: +async def get_cookies(server_url, session, session_http: ClientSession = None) -> list: """Get the page cookies""" try: url = f"{server_url}/session/{session}/cookie" - response = await __get(url) + response = await _get(url, session_http=session_http) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get page cookies.") from error + except Exception as e: + raise WebDriverError("Failed to get page cookies.") from e -async def close_session(server_url, session): +async def close_session(server_url, session, session_http: ClientSession = None): """Close an opened session and close the browser""" try: url = f"{server_url}/session/{session}" - await __delete(url) + await _delete(url, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to close session.") from error + except Exception as e: + raise WebDriverError("Failed to close session.") from e -async def get(server_url, session, page_url): +async def get(server_url, session, page_url, session_http: ClientSession = None): """Does the same of 'go_to_page'. Added to be compatible with selenium method name'""" - return go_to_page(server_url, session, page_url) + return go_to_page(server_url, session, page_url, session_http=session_http) -async def go_to_page(server_url, session, page_url): +async def go_to_page(server_url, session, page_url, session_http:ClientSession=None): """Navigate to 'page_url'""" try: url = f"{server_url}/session/{session}/url" payload = {"url": page_url} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError(f"Failed to navigate to page '{page_url}'.") from error + except Exception as e: + raise WebDriverError(f"Failed to navigate to page '{page_url}'.") from e -async def send_keys(server_url, session, element, text): +async def send_keys( + server_url, session, element, text, session_http: ClientSession = None +): """Fill an editable element, for example a textarea, with a given text""" try: url = f"{server_url}/session/{session}/element/{element}/value" payload = {"text": text, "value": [*text], "id": element} - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError(f"Failed to send key '{text}'.") from error + except Exception as e: + raise WebDriverError(f"Failed to send key '{text}'.") from e -async def click(server_url, session, element): +async def click(server_url, session, element, session_http: ClientSession = None): """Click on an element""" try: payload = {"id": element} url = f"{server_url}/session/{session}/element/{element}/click" - await __post(url, payload) + await _post(url, payload, session_http=session_http) return True - except Exception as error: - raise WebDriverError("Failed to click on element.") from error + except Exception as e: + raise WebDriverError("Failed to click on element.") from e -async def find_element(server_url, session, locator_type, locator_value) -> dict: +async def find_element( + server_url, session, locator_type, locator_value, session_http: ClientSession = None +) -> dict: """Find an element by a 'locator', for example 'xpath'""" try: payload = {"using": locator_type, "value": locator_value} url = f"{server_url}/session/{session}/element" - response = await __post(url, payload) + response = await _post(url, payload, session_http=session_http) # Firefox does not support id locator, so it prints the error message to the user # It helps on debug if response.get("value").get("error"): raise WebDriverError(f"Failed to find element. {response}") - return __helper.get_element(response) - except Exception as error: + return get_element(response) + except Exception as e: raise WebDriverError( f"Failed to find element by '{locator_type}'-'{locator_value}'." - ) from error + ) from e -async def get_session(server_url: str, capabilities: Optional[dict] = None) -> str: +async def get_session( + server_url: str, + capabilities: Optional[dict] = None, + session_http: ClientSession = None, +) -> str: """ Opens a browser and a session. This session is used for all functions to perform events in the page @@ -791,9 +917,9 @@ async def get_session(server_url: str, capabilities: Optional[dict] = None) -> s if not capabilities: capabilities = {} url = f"{server_url}/session" - response = await __post(url, capabilities) + response = await _post(url, capabilities, session_http=session_http) return response.get("sessionId") - except Exception as error: + except Exception as e: raise WebDriverError( "Failed to open session. Check the browser capabilities." - ) from error + ) from e diff --git a/caqui/easy/action_chains.py b/caqui/easy/action_chains.py index 7a5a33f..e278c5f 100644 --- a/caqui/easy/action_chains.py +++ b/caqui/easy/action_chains.py @@ -5,38 +5,40 @@ class ActionChains: def __init__(self, driver) -> None: - self.__remote = driver.remote - self.__session = driver.session - self.__coroutines: list[Coroutine] = [] + self._remote = driver.remote + self._session = driver.session + self._session_http = driver.session_http + self._coroutines: list[Coroutine] = [] + self._element = None def click(self, element: Element): """ Clicks on the element `element` """ - self.__element = element - coroutine = asynchronous.click(self.__remote, self.__session, str(element)) - self.__coroutines.append(coroutine) + self._element = element + coroutine = asynchronous.click(self._remote, self._session, str(element), session_http=self._session_http) + self._coroutines.append(coroutine) return self def move_to_element(self, element: Element): """Move the mouve to the element `element`""" - self.__element = element + self._element = element coroutine = asynchronous.actions_move_to_element( - self.__remote, self.__session, str(element) + self._remote, self._session, str(element), session_http=self._session_http ) - self.__coroutines.append(coroutine) + self._coroutines.append(coroutine) return self def scroll_to_element(self, element: Element): """Scrolls the screen to the element `element`""" - self.__element = element + self._element = element coroutine = asynchronous.actions_scroll_to_element( - self.__remote, self.__session, str(element) + self._remote, self._session, str(element), session_http=self._session_http ) - self.__coroutines.append(coroutine) + self._coroutines.append(coroutine) return self async def perform(self): """Executes the chain of Coroutines""" - [await coroutine for coroutine in self.__coroutines] + [await coroutine for coroutine in self._coroutines] return True diff --git a/caqui/easy/alert.py b/caqui/easy/alert.py index 9cafcb0..d1ba6f7 100644 --- a/caqui/easy/alert.py +++ b/caqui/easy/alert.py @@ -3,22 +3,23 @@ class Alert: def __init__(self, driver) -> None: - self.__remote = driver.remote - self.__session = driver.session + self._remote = driver.remote + self._session = driver.session + self._session_http = driver.session_http @property def text(self): """Returns the text of the alert""" - return synchronous.get_alert_text(self.__remote, self.__session) + return synchronous.get_alert_text(self._remote, self._session) async def accept(self): """Accepts the alert""" - return await asynchronous.accept_alert(self.__remote, self.__session) + return await asynchronous.accept_alert(self._remote, self._session, session_http=self._session_http) async def dismiss(self): """Closes the alert ignoring it""" - return await asynchronous.dismiss_alert(self.__remote, self.__session) + return await asynchronous.dismiss_alert(self._remote, self._session, session_http=self._session_http) async def send_keys(self, text): """Send a text to a textbox in the alert""" - return await asynchronous.send_alert_text(self.__remote, self.__session, text) + return await asynchronous.send_alert_text(self._remote, self._session, text, session_http=self._session_http) diff --git a/caqui/easy/element.py b/caqui/easy/element.py index f3056f6..6ade2fa 100644 --- a/caqui/easy/element.py +++ b/caqui/easy/element.py @@ -4,41 +4,42 @@ class Element: def __init__(self, element, driver) -> None: - self.__element = element - self.__remote = driver.remote - self.__session = driver.session - self.__driver = driver + self._element = element + self._remote = driver.remote + self._session = driver.session + self._session_http = driver.session_http + self._driver = driver def __str__(self) -> str: - return self.__element + return self._element @property def rect(self): """Returns the rectangle that enclosed the element For example: {"height": 23, "width": 183, "x": 10, "y": 9652.12} """ - return synchronous.get_rect(self.__remote, self.__session, self.__element) + return synchronous.get_rect(self._remote, self._session, self._element) @property def tag_name(self): """Returns the tag name of the element""" - return synchronous.get_tag_name(self.__remote, self.__session, self.__element) + return synchronous.get_tag_name(self._remote, self._session, self._element) @property def text(self): """Returns the text of the element""" - return synchronous.get_text(self.__remote, self.__session, self.__element) + return synchronous.get_text(self._remote, self._session, self._element) @property def active_element(self): """Returns the active element""" - self.__element = synchronous.get_active_element(self.__driver, self.__session) - return self.__element + self._element = synchronous.get_active_element(self._driver, self._session) + return self._element async def value_of_css_property(self, property_name): """Returns the desired CSS property of the element""" return await asynchronous.get_css_value( - self.__remote, self.__session, self.__element, property_name + self._remote, self._session, self._element, property_name, session_http=self._session_http ) async def screenshot(self, file): @@ -48,70 +49,70 @@ async def screenshot(self, file): path = "./" file_name = os.path.basename(file) return await asynchronous.take_screenshot_element( - self.__remote, self.__session, self.__element, path, file_name + self._remote, self._session, self._element, path, file_name, session_http=self._session_http ) async def is_selected(self) -> bool: """Returns True if the element is selected. Otherwise returns False""" - return await asynchronous.is_element_selected(self.__remote, self.__session, self.__element) + return await asynchronous.is_element_selected(self._remote, self._session, self._element, session_http=self._session_http) async def is_enabled(self): """Returns True if the element is enabled. Otherwise returns False""" - return await asynchronous.is_element_enabled(self.__remote, self.__session, self.__element) + return await asynchronous.is_element_enabled(self._remote, self._session, self._element, session_http=self._session_http) async def get_text(self): """Returns the text of the element""" - return await asynchronous.get_text(self.__remote, self.__session, self.__element) + return await asynchronous.get_text(self._remote, self._session, self._element, session_http=self._session_http) async def get_css_value(self, property_name): """Returns the desired CSS property of the element""" return await asynchronous.get_css_value( - self.__remote, self.__session, self.__element, property_name + self._remote, self._session, self._element, property_name, session_http=self._session_http ) async def submit(self): """Submits a form""" - return await asynchronous.submit(self.__remote, self.__session, self.__element) + return await asynchronous.submit(self._remote, self._session, self._element, session_http=self._session_http) async def get_rect(self): """Returns the rectangle that enclosed the element""" - return await asynchronous.get_rect(self.__remote, self.__session, self.__element) + return await asynchronous.get_rect(self._remote, self._session, self._element, session_http=self._session_http) async def get_tag_name(self): """Returns the element tag name""" - return await asynchronous.get_tag_name(self.__remote, self.__session, self.__element) + return await asynchronous.get_tag_name(self._remote, self._session, self._element, session_http=self._session_http) async def get_computed_label(self): """Get the element tag computed label. Get the accessibility name""" - return await asynchronous.get_computed_label(self.__remote, self.__session, self.__element) + return await asynchronous.get_computed_label(self._remote, self._session, self._element, session_http=self._session_http) async def get_computed_role(self): """Get the element tag computed role (the element role)""" - return await asynchronous.get_computed_role(self.__remote, self.__session, self.__element) + return await asynchronous.get_computed_role(self._remote, self._session, self._element, session_http=self._session_http) async def get_property(self, property): """Get the given HTML property of an element, for example, 'href'""" return await asynchronous.get_property( - self.__remote, self.__session, self.__element, property + self._remote, self._session, self._element, property, session_http=self._session_http ) async def get_attribute(self, attribute): """Get the given HTML attribute of an element, for example, 'aria-valuenow'""" return await asynchronous.get_attribute( - self.__remote, self.__session, self.__element, attribute + self._remote, self._session, self._element, attribute, session_http=self._session_http ) async def clear(self): """Clear the element text""" - return await asynchronous.clear_element(self.__remote, self.__session, self.__element) + return await asynchronous.clear_element(self._remote, self._session, self._element, session_http=self._session_http) async def send_keys(self, text): """Fill the element with a text""" - return await asynchronous.send_keys(self.__remote, self.__session, self.__element, text) + return await asynchronous.send_keys(self._remote, self._session, self._element, text, session_http=self._session_http) async def click(self): """Click on the element""" - return await asynchronous.click(self.__remote, self.__session, self.__element) + return await asynchronous.click(self._remote, self._session, self._element, session_http=self._session_http) async def find_elements(self, locator, value): """ @@ -122,15 +123,15 @@ async def find_elements(self, locator, value): """ result = [] elements = await asynchronous.find_children_elements( - self.__remote, self.__session, self.__element, locator, value + self._remote, self._session, self._element, locator, value, session_http=self._session_http ) for element in elements: - result.append(Element(element, self.__driver)) + result.append(Element(element, self._driver)) return result async def find_element(self, locator, value): """Find the element by `locator_type`""" element = await asynchronous.find_child_element( - self.__remote, self.__session, self.__element, locator, value + self._remote, self._session, self._element, locator, value, session_http=self._session_http ) - return Element(element, self.__driver) + return Element(element, self._driver) diff --git a/caqui/easy/page.py b/caqui/easy/page.py index bfaadc6..6a877cb 100644 --- a/caqui/easy/page.py +++ b/caqui/easy/page.py @@ -1,5 +1,7 @@ import os from typing import Optional, Union + +from aiohttp import ClientSession from caqui import asynchronous, synchronous from caqui.easy.action_chains import ActionChains from caqui.easy.window import Window @@ -11,41 +13,42 @@ class AsyncPage: def __init__( - self, server_url: str, capabilities: Optional[dict] = None, url: Union[str, None] = None + self, server_url: str, capabilities: Optional[dict] = None, url: Union[str, None] = None, session_http: ClientSession=None ) -> None: """Mimics Selenium methods""" + self.session_http = session_http if not capabilities: capabilities = {} if not isinstance(capabilities, dict): raise CapabilityNotSupported("Expected dictionary") - self.__server_url = server_url - self.__session = synchronous.get_session(server_url, capabilities) + self._server_url = server_url + self._session = synchronous.get_session(server_url, capabilities) if url: synchronous.get( - self.__server_url, - self.__session, + self._server_url, + self._session, url, ) @property def remote(self) -> str: """Returns the Driver Server URL""" - return self.__server_url + return self._server_url @property def session(self) -> str: """Returns tne session id""" - return self.__session + return self._session @property def title(self): """Returns the title of the page""" - return synchronous.get_title(self.__server_url, self.__session) + return synchronous.get_title(self._server_url, self._session) @property def current_url(self): """Returns the current URL of the page""" - return synchronous.get_url(self.__server_url, self.__session) + return synchronous.get_url(self._server_url, self._session) @property def window(self): @@ -70,45 +73,45 @@ def switch_to(self): @property def window_handles(self): """Returns the window handles""" - return synchronous.get_window_handles(self.__server_url, self.__session) + return synchronous.get_window_handles(self._server_url, self._session) @property def current_window_handle(self): """Returns the current window handle""" - return synchronous.get_window(self.__server_url, self.__session) + return synchronous.get_window(self._server_url, self._session) def quit(self): """Closes the session""" - synchronous.close_session(self.__server_url, self.__session) + synchronous.close_session(self._server_url, self._session) async def close(self): """Closes the window""" - return await asynchronous.close_window(self.__server_url, self.__session) + return await asynchronous.close_window(self._server_url, self._session, session_http=self.session_http) async def execute_script(self, script, args=[]): - return await asynchronous.execute_script(self.__server_url, self.__session, script, args) + return await asynchronous.execute_script(self._server_url, self._session, script, args, session_http=self.session_http) async def set_window_position(self, x, y): """Repositions the page""" - rect = await asynchronous.get_window_rectangle(self.__server_url, self.__session) + rect = await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) return await asynchronous.set_window_rectangle( - self.__server_url, self.__session, rect.get("width"), rect.get("height"), x, y + self._server_url, self._session, rect.get("width"), rect.get("height"), x, y, session_http=self.session_http ) async def set_window_size(self, width, height): """Resizes the page""" - rect = await asynchronous.get_window_rectangle(self.__server_url, self.__session) + rect = await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) return await asynchronous.set_window_rectangle( - self.__server_url, self.__session, width, height, rect.get("x"), rect.get("y") + self._server_url, self._session, width, height, rect.get("x"), rect.get("y"), session_http=self.session_http ) async def get_window_position(self): """Returns the window rectangle""" - return await asynchronous.get_window_rectangle(self.__server_url, self.__session) + return await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) async def get_window_size(self): """Returns the window rectangle""" - return await asynchronous.get_window_rectangle(self.__server_url, self.__session) + return await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) async def save_screenshot(self, file): """Takes a scheenshot of the page""" @@ -117,71 +120,71 @@ async def save_screenshot(self, file): path = "./" file_name = os.path.basename(file) return await asynchronous.take_screenshot( - self.__server_url, self.__session, path, file_name + self._server_url, self._session, path, file_name, session_http=self.session_http ) async def delete_all_cookies(self): """Deletes all storaged cookies""" - return await asynchronous.delete_all_cookies(self.__server_url, self.__session) + return await asynchronous.delete_all_cookies(self._server_url, self._session, session_http=self.session_http) async def delete_cookie(self, cookie_name): """Delete the desired cookie""" - return await asynchronous.delete_cookie(self.__server_url, self.__session, cookie_name) + return await asynchronous.delete_cookie(self._server_url, self._session, cookie_name, session_http=self.session_http) async def get_cookies(self): """Get all cookies""" - return await asynchronous.get_cookies(self.__server_url, self.__session) + return await asynchronous.get_cookies(self._server_url, self._session, session_http=self.session_http) async def get_cookie(self, cookie_name): """Get the desired cookie""" - return await asynchronous.get_named_cookie(self.__server_url, self.__session, cookie_name) + return await asynchronous.get_named_cookie(self._server_url, self._session, cookie_name, session_http=self.session_http) async def add_cookie(self, cookie): """Add a new cookie""" - return await asynchronous.add_cookie(self.__server_url, self.__session, cookie) + return await asynchronous.add_cookie(self._server_url, self._session, cookie, session_http=self.session_http) async def implicitly_wait(self, timeouts: int): """Set implicty timeouts""" - return await asynchronous.set_timeouts(self.__server_url, self.__session, timeouts) + return await asynchronous.set_timeouts(self._server_url, self._session, timeouts, session_http=self.session_http) async def back(self): """This command causes the browser to traverse one step backward in the joint session history of the current browse. This is equivalent to pressing the back button in the browser.""" - return await asynchronous.go_back(self.__server_url, self.__session) + return await asynchronous.go_back(self._server_url, self._session, session_http=self.session_http) async def forward(self): """Go page forward""" - return await asynchronous.go_forward(self.__server_url, self.__session) + return await asynchronous.go_forward(self._server_url, self._session, session_http=self.session_http) async def refresh(self): """Refreshs the page""" - return await asynchronous.refresh_page(self.__server_url, self.__session) + return await asynchronous.refresh_page(self._server_url, self._session, session_http=self.session_http) async def fullscreen_window(self): """Sets the page in fullscreen""" - return await asynchronous.fullscreen_window(self.__server_url, self.__session) + return await asynchronous.fullscreen_window(self._server_url, self._session, session_http=self.session_http) async def minimize_window(self): """Minimizes the page""" - return await asynchronous.minimize_window(self.__server_url, self.__session) + return await asynchronous.minimize_window(self._server_url, self._session, session_http=self.session_http) async def maximize_window(self): """Maximizes the page""" - return await asynchronous.maximize_window(self.__server_url, self.__session) + return await asynchronous.maximize_window(self._server_url, self._session, session_http=self.session_http) async def get(self, url): """Navigates to URL `url`""" await asynchronous.go_to_page( - self.__server_url, - self.__session, - url, + self._server_url, + self._session, + url,session_http=self.session_http ) async def find_elements(self, locator, value): """Search the DOM elements by 'locator', for example, 'xpath'""" elements = await asynchronous.find_elements( - self.__server_url, self.__session, locator, value + self._server_url, self._session, locator, value, session_http=self.session_http ) result = [] for element in elements: @@ -190,5 +193,5 @@ async def find_elements(self, locator, value): async def find_element(self, locator, value): """Find an element by a 'locator', for example 'xpath'""" - element = await asynchronous.find_element(self.__server_url, self.__session, locator, value) + element = await asynchronous.find_element(self._server_url, self._session, locator, value, session_http=self.session_http) return Element(element, self) diff --git a/caqui/easy/switch_to.py b/caqui/easy/switch_to.py index 81eaa9a..afb539a 100644 --- a/caqui/easy/switch_to.py +++ b/caqui/easy/switch_to.py @@ -5,47 +5,48 @@ class SwitchTo: def __init__(self, driver) -> None: - self.__driver = driver - self.__iframe = None - self.__window_handle = None + self._driver = driver + self._iframe = None + self._window_handle = None + self._session_http = driver.session_http @property def active_element(self): """Returns the active element""" - element = synchronous.get_active_element(self.__driver.remote, self.__driver.session) - return Element(element, self.__driver) + element = synchronous.get_active_element(self._driver.remote, self._driver.session) + return Element(element, self._driver) @property def alert(self): """Returns the `Alert` object""" - return Alert(self.__driver) + return Alert(self._driver) async def new_window(self, window_type): """Opens a new window""" - self.__window_handle = await asynchronous.new_window( - self.__driver.remote, self.__driver.session, window_type + self._window_handle = await asynchronous.new_window( + self._driver.remote, self._driver.session, window_type, session_http=self._session_http ) - self.__window_handle = await asynchronous.switch_to_window( - self.__driver.remote, self.__driver.session, self.__window_handle + self._window_handle = await asynchronous.switch_to_window( + self._driver.remote, self._driver.session, self._window_handle, session_http=self._session_http ) - return self.__window_handle + return self._window_handle async def window(self, window_handle): """Switchs to window `window_handle`""" - self.__window_handle = await asynchronous.switch_to_window( - self.__driver.remote, self.__driver.session, window_handle + self._window_handle = await asynchronous.switch_to_window( + self._driver.remote, self._driver.session, window_handle, session_http=self._session_http ) - return self.__window_handle + return self._window_handle async def frame(self, iframe): """Switches to frame `iframe`""" - self.__iframe = str(iframe) + self._iframe = str(iframe) return await asynchronous.switch_to_frame( - self.__driver.remote, self.__driver.session, self.__iframe + self._driver.remote, self._driver.session, self._iframe, session_http=self._session_http ) async def default_content(self): """Switches to parent frame of 'element_frame'""" return await asynchronous.switch_to_parent_frame( - self.__driver.remote, self.__driver.session, self.__iframe + self._driver.remote, self._driver.session, self._iframe, session_http=self._session_http ) diff --git a/tests/conftest.py b/tests/conftest.py index 61e1444..f4830e8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,4 @@ +from aiohttp import ClientSession from pytest import fixture from tests.constants import PAGE_URL from caqui.easy import AsyncPage @@ -5,6 +6,7 @@ from caqui.easy.options import ChromeOptionsBuilder from caqui.easy.server import Server from caqui import synchronous +import pytest_asyncio SERVER_PORT = 9999 SERVER_URL = f"http://localhost:{SERVER_PORT}" @@ -48,16 +50,16 @@ def setup_functional_environment(): finally: synchronous.close_session(server_url, session) - -@fixture -def setup_environment(): +@pytest_asyncio.fixture +async def setup_environment(): server_url = SERVER_URL capabilities = __build_capabilities() - page = AsyncPage(server_url, capabilities, PAGE_URL) - yield page - try: - synchronous.dismiss_alert(server_url, page.session) - except Exception: - pass - finally: - page.quit() + async with ClientSession() as session_http: + page = AsyncPage(server_url, capabilities, PAGE_URL, session_http=session_http) + yield page + try: + synchronous.dismiss_alert(server_url, page.session) + except Exception: + pass + finally: + page.quit() diff --git a/tests/feature/test_async_with_http_session.py b/tests/feature/test_async_with_http_session.py new file mode 100644 index 0000000..d35c5fc --- /dev/null +++ b/tests/feature/test_async_with_http_session.py @@ -0,0 +1,1245 @@ +from aiohttp import ClientSession +from pytest import mark, raises +from caqui import asynchronous, synchronous +from caqui.exceptions import WebDriverError +from caqui.by import By +from tests.constants import COOKIE +from caqui.easy import AsyncPage +from tests.constants import PAGE_URL +from pytest import mark +from tests.constants import COOKIE + + +@mark.asyncio +async def test_big_scenario_of_functions_with_session_http(setup_environment: AsyncPage): + page = setup_environment + await page.implicitly_wait(10) + + # Need to navigate to a web page. If use 'playgound.html' the error + # 'Document is cookie-averse' happens + await page.get( + "https://example.org/", + ) + cookies = COOKIE + await page.add_cookie(cookies) + cookie = (await page.get_cookies())[0] + cookie["name"] = "other" + await page.add_cookie(cookie) + await page.delete_cookie("other") + await page.delete_all_cookies() + await page.get( + PAGE_URL, + ) + + await page.switch_to.active_element.get_attribute("value") + element = await page.find_element(By.XPATH, "//a") + # Returns and base64 encoded string into image + await element.screenshot("/tmp/image.png") + + await page.back() + await page.forward() + await page.refresh() + + alert_element = await page.find_element(By.CSS_SELECTOR, "#alert-button-prompt") + await alert_element.click() + alert_object = page.switch_to.alert + await page.alert.accept() + + await alert_element.click() + await alert_object.send_keys("Caqui") + await alert_object.dismiss() + + iframe = await page.find_element(By.ID, "my-iframe") + # switch to selected iframe + await page.switch_to.frame(iframe) + await page.switch_to.default_content() + # switching to second iframe based on index + iframe = (await page.find_elements(By.ID, "my-iframe"))[0] + + # switch to selected iframe + await page.switch_to.frame(iframe) + # switch back to default content + await page.switch_to.default_content() + + window_handle = page.current_window_handle + assert len(page.window_handles) >= 1 + await page.switch_to.window(window_handle) + # Opens a new tab and switches to new tab + await page.switch_to.new_window("tab") + # Opens a new window and switches to new window + await page.switch_to.new_window("window") + + # Access each dimension individually + await page.set_window_size(1024, 768) + # Move the window to the top left of the primary monitor + await page.set_window_position(0, 0) + await page.maximize_window() + # await driver.minimize_window() # does not work on headless mode + await page.save_screenshot("/tmp/image.png") + + # Executing JavaScript to capture innerText of header element + await page.execute_script('alert("any warn")') + + + +@mark.asyncio +async def test_add_cookie(setup_functional_environment): + server_url, session = setup_functional_environment + # Need to navigate to a web page. If use 'playgound.html' the error + # 'Document is cookie-averse' happens + synchronous.go_to_page( + server_url, + session, + "https://example.org/", + ) + cookie = COOKIE + assert synchronous.add_cookie(server_url, session, cookie) is True + cookies_after = synchronous.get_cookies(server_url, session) + assert len(cookies_after) > 0 + + cookies_before = cookies_after + cookie = cookies_before[0] + cookie[By.NAME] = "another" + + async with ClientSession() as session_http: + assert ( + await asynchronous.add_cookie( + server_url, session, cookie, session_http=session_http + ) + is True + ) + cookies_after = synchronous.get_cookies(server_url, session) + assert len(cookies_after) > len(cookies_before) + + +@mark.skip(reason="works just in firefox") +@mark.asyncio +async def test_delete_cookie_asynchronous(setup_functional_environment): + server_url, session = setup_functional_environment + cookies = synchronous.get_cookies(server_url, session) + name = cookies[0].get(By.NAME) + zero = 0 + + async with ClientSession() as session_http: + assert ( + await asynchronous.delete_cookie( + server_url, session, name, session_http=session_http + ) + is True + ) + cookies = synchronous.get_cookies(server_url, session) + assert len(cookies) == zero + + +@mark.skip(reason="works just in firefox") +@mark.asyncio +def test_delete_cookie_synchronous(setup_functional_environment): + server_url, session = setup_functional_environment + cookies = synchronous.get_cookies(server_url, session) + name = cookies[0].get(By.NAME) + zero = 0 + + assert synchronous.delete_cookie(server_url, session, name) is True + cookies = synchronous.get_cookies(server_url, session) + assert len(cookies) == zero + + +@mark.asyncio +async def test_refresh_page(setup_functional_environment): + server_url, session = setup_functional_environment + + element_before = synchronous.find_element(server_url, session, By.XPATH, "//input") + assert ( + synchronous.refresh_page( + server_url, + session, + ) + is True + ) + + element_after = synchronous.find_element(server_url, session, By.XPATH, "//input") + assert element_before != element_after + + element_before = element_after + async with ClientSession() as session_http: + assert ( + await asynchronous.refresh_page( + server_url, session, session_http=session_http + ) + is True + ) + + element_after = synchronous.find_element(server_url, session, By.XPATH, "//input") + assert element_before != element_after + + +@mark.asyncio +async def test_go_forward(setup_functional_environment): + server_url, session = setup_functional_environment + title = "Sample page" + + synchronous.go_back(server_url, session) + assert ( + synchronous.go_forward( + server_url, + session, + ) + is True + ) + assert synchronous.get_title(server_url, session) == title + + synchronous.go_back(server_url, session) + async with ClientSession() as session_http: + assert ( + await asynchronous.go_forward( + server_url, session, session_http=session_http + ) + is True + ) + assert synchronous.get_title(server_url, session) == title + + +@mark.asyncio +async def test_set_window_rectangle(setup_functional_environment): + server_url, session = setup_functional_environment + width = 500 + height = 300 + window_rectangle_before = synchronous.get_window_rectangle(server_url, session) + x = window_rectangle_before.get("x") + 1 + y = window_rectangle_before.get("y") + 1 + + assert ( + synchronous.set_window_rectangle(server_url, session, width, height, x, y) + is True + ) + + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") != window_rectangle_before.get("height") + assert window_rectangle_after.get("width") != window_rectangle_before.get("width") + assert window_rectangle_after.get("x") != window_rectangle_before.get("x") + assert window_rectangle_after.get("y") != window_rectangle_before.get("y") + + synchronous.maximize_window(server_url, session) + + async with ClientSession() as session_http: + assert ( + await asynchronous.set_window_rectangle( + server_url, session, width, height, x, y, session_http=session_http + ) + is True + ) + + window_rectangle_after = None + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") != window_rectangle_before.get("height") + assert window_rectangle_after.get("width") != window_rectangle_before.get("width") + assert window_rectangle_after.get("x") != window_rectangle_before.get("x") + assert window_rectangle_after.get("y") != window_rectangle_before.get("y") + + +@mark.skip(reason="does not work in headless mode") +@mark.asyncio +async def test_fullscreen_window(setup_functional_environment): + server_url, session = setup_functional_environment + window_rectangle_before = synchronous.get_window_rectangle(server_url, session) + + assert synchronous.fullscreen_window(server_url, session) is True + + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") > window_rectangle_before.get("height") + assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + + synchronous.maximize_window(server_url, session) + + async with ClientSession() as session_http: + assert ( + await asynchronous.fullscreen_window( + server_url, session, session_http=session_http + ) + is True + ) + + window_rectangle_after = None + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") > window_rectangle_before.get("height") + assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + + +@mark.skip(reason="does not work in headless mode") +@mark.asyncio +async def test_minimize_window(setup_functional_environment): + server_url, session = setup_functional_environment + window_rectangle_before = synchronous.get_window_rectangle(server_url, session) + + assert synchronous.minimize_window(server_url, session) is True + + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") < window_rectangle_before.get("height") + assert window_rectangle_after.get("width") < window_rectangle_before.get("width") + + synchronous.maximize_window(server_url, session) + + async with ClientSession() as session_http: + assert ( + await asynchronous.minimize_window( + server_url, session, session_http=session_http + ) + is True + ) + + window_rectangle_after = None + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") < window_rectangle_before.get("height") + assert window_rectangle_after.get("width") < window_rectangle_before.get("width") + + +@mark.skip(reason="does not work in headless mode") +@mark.asyncio +async def test_maximize_window_asynchronous(setup_functional_environment): + server_url, session = setup_functional_environment + window_rectangle_before = synchronous.get_window_rectangle(server_url, session) + + async with ClientSession() as session_http: + assert ( + await asynchronous.maximize_window( + server_url, session, session_http=session_http + ) + is True + ) + + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") > window_rectangle_before.get("height") + assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + + +@mark.skip(reason="does not work in headless mode") +@mark.asyncio +def test_maximize_window_synchronous(setup_functional_environment): + server_url, session = setup_functional_environment + window_rectangle_before = synchronous.get_window_rectangle(server_url, session) + + assert synchronous.maximize_window(server_url, session) is True + + window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + assert window_rectangle_after != window_rectangle_before + assert window_rectangle_after.get("height") > window_rectangle_before.get("height") + assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + + +@mark.parametrize("window_type", ("tab", "window")) +@mark.asyncio +async def test_switch_to_window(setup_functional_environment, window_type): + server_url, session = setup_functional_environment + + synchronous.new_window(server_url, session, window_type) + handles = synchronous.get_window_handles(server_url, session) + sample_page = handles[0] + new_page = handles[1] + + assert synchronous.switch_to_window(server_url, session, handle=new_page) is True + assert synchronous.get_title(server_url, session) == "" + synchronous.switch_to_window(server_url, session, handle=sample_page) is True + + async with ClientSession() as session_http: + assert ( + await asynchronous.switch_to_window( + server_url, session, handle=new_page, session_http=session_http + ) + is True + ) + assert synchronous.get_title(server_url, session) == "" + + +@mark.parametrize("window_type", ("tab", "window")) +@mark.asyncio +async def test_new_window(setup_functional_environment, window_type): + server_url, session = setup_functional_environment + + assert synchronous.new_window(server_url, session, window_type) is not None + import time + + time.sleep(3) + async with ClientSession() as session_http: + assert ( + await asynchronous.new_window( + server_url, session, window_type, session_http=session_http + ) + is not None + ) + + +@mark.asyncio +async def test_switch_to_parent_frame_asynchronous(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.ID + locator_value = "my-iframe" + + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + async with ClientSession() as session_http: + assert ( + await asynchronous.switch_to_parent_frame( + server_url, session, element_frame, session_http=session_http + ) + is True + ) + + +def test_switch_to_parent_frame_synchronous(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.ID + locator_value = "my-iframe" + + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + assert ( + synchronous.switch_to_parent_frame(server_url, session, element_frame) is True + ) + + +@mark.asyncio +async def test_switch_to_frame_asynchronous(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.ID + locator_value = "my-iframe" + + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + async with ClientSession() as session_http: + assert ( + await asynchronous.switch_to_frame(server_url, session, element_frame, session_http=session_http) is True + ) + + +def test_switch_to_frame_synchronous(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.ID + locator_value = "my-iframe" + + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + assert synchronous.switch_to_frame(server_url, session, element_frame) is True + + +@mark.asyncio +async def test_send_alert_text(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.CSS_SELECTOR + locator_value = "#alert-button-prompt" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + synchronous.click(server_url, session, element) + + assert synchronous.send_alert_text(server_url, session, text="any1") is True + synchronous.accept_alert(server_url, session) is True + + synchronous.click(server_url, session, element) + async with ClientSession() as session_http: + assert await asynchronous.send_alert_text(server_url, session, "any2", session_http=session_http) is True + synchronous.accept_alert(server_url, session) is True + + +@mark.asyncio +async def test_accept_alert(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.CSS_SELECTOR + locator_value = "#alert-button" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + synchronous.click(server_url, session, element) + + assert synchronous.accept_alert(server_url, session) is True + + synchronous.click(server_url, session, element) + async with ClientSession() as session_http: + assert await asynchronous.accept_alert(server_url, session, session_http=session_http) is True + + +@mark.asyncio +async def test_dismiss_alert(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.CSS_SELECTOR + locator_value = "#alert-button" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + synchronous.click(server_url, session, element) + + assert synchronous.dismiss_alert(server_url, session) is True + + synchronous.click(server_url, session, element) + async with ClientSession() as session_http: + assert await asynchronous.dismiss_alert(server_url, session, session_http=session_http) is True + + +@mark.asyncio +async def test_take_screenshot_element(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.CSS_SELECTOR + locator_value = "#alert-button" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.take_screenshot_element(server_url, session, element) is True + async with ClientSession() as session_http: + assert ( + await asynchronous.take_screenshot_element( + server_url, session, element, session_http=session_http + ) + is True + ) + + +@mark.asyncio +async def test_take_screenshot(setup_functional_environment): + server_url, session = setup_functional_environment + + assert synchronous.take_screenshot(server_url, session) is True + async with ClientSession() as session_http: + assert ( + await asynchronous.take_screenshot( + server_url, session, session_http=session_http + ) + is True + ) + + +@mark.skip(reason="works just in firefox") +@mark.asyncio +async def test_delete_cookies_asynchronous(setup_functional_environment): + server_url, session = setup_functional_environment + + cookies_before = synchronous.get_cookies(server_url, session) + + async with ClientSession() as session_http: + response = await asynchronous.delete_all_cookies(server_url, session, session_http=session_http) + assert response is True + + cookies_after = synchronous.get_cookies(server_url, session) + assert len(cookies_before) != len(cookies_after) + + +@mark.skip(reason="works just in firefox") +@mark.asyncio +async def test_delete_cookies_synchronous(setup_functional_environment): + server_url, session = setup_functional_environment + + cookies_before = synchronous.get_cookies(server_url, session) + + assert synchronous.delete_all_cookies(server_url, session) is True + + cookies_after = synchronous.get_cookies(server_url, session) + assert len(cookies_before) != len(cookies_after) + + +@mark.skip(reason="works just with Firefox") +@mark.asyncio +async def test_get_named_cookie(setup_functional_environment): + server_url, session = setup_functional_environment + name = "username" # cookie created on page load + expected = "John Doe" + + assert ( + synchronous.get_named_cookie(server_url, session, name).get("value") == expected + ) + async with ClientSession() as session_http: + response = await asynchronous.get_named_cookie(server_url, session, name, session_http=session_http) + assert response.get("value") == expected + + +@mark.asyncio +async def test_get_computed_label(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.CSS_SELECTOR + locator_value = "#alert-button" + expected = "alert" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.get_computed_label(server_url, session, element) == expected + + async with ClientSession() as session_http: + assert ( + await asynchronous.get_computed_label(server_url, session, element, session_http=session_http) == expected + ) + + +@mark.asyncio +async def test_get_computed_role(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + expected = "textbox" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.get_computed_role(server_url, session, element) == expected + + async with ClientSession() as session_http: + assert ( + await asynchronous.get_computed_role(server_url, session, element, session_http=session_http) == expected + ) + + +@mark.asyncio +async def test_get_tag_name(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + expected = "input" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.get_tag_name(server_url, session, element) == expected + async with ClientSession() as session_http: + assert await asynchronous.get_tag_name(server_url, session, element, session_http=session_http) == expected + + +@mark.parametrize( + "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] +) +@mark.asyncio +async def test_find_element_from_shadow_root( + setup_functional_environment, locator, value +): + server_url, session = setup_functional_environment + locator_type = By.ID + locator_value = "shadow-root" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + shadow_root = synchronous.get_shadow_root(server_url, session, element) + + actual = synchronous.find_child_element( + server_url, session, shadow_root, locator, value + ) + + assert actual is not None + async with ClientSession() as session_http: + actual = await asynchronous.find_child_element( + server_url, session, shadow_root, locator, value, session_http=session_http + ) + + assert actual is not None + + +@mark.parametrize( + "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] +) +@mark.asyncio +async def test_find_elements_from_shadow_root( + setup_functional_environment, locator, value +): + server_url, session = setup_functional_environment + locator_type = By.ID + locator_value = "shadow-root" + one = 1 + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + shadow_root = synchronous.get_shadow_root(server_url, session, element) + + actual = synchronous.find_children_elements( + server_url, session, shadow_root, locator, value + ) + + assert len(actual) == one + + async with ClientSession() as session_http: + actual = await asynchronous.find_children_elements( + server_url, session, shadow_root, locator, value, session_http=session_http + ) + + assert len(actual) == one + + +@mark.asyncio +async def test_get_shadow_root(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.ID + locator_value = "shadow-root" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.get_shadow_root(server_url, session, element) is not None + + async with ClientSession() as session_http: + response = await asynchronous.get_shadow_root(server_url, session, element, session_http=session_http) + assert response is not None + + +@mark.asyncio +async def test_get_rect(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + expected = {"height": 21, "width": 185, "x": 8, "y": 100.4375} + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.get_rect(server_url, session, element) == expected + async with ClientSession() as session_http: + assert await asynchronous.get_rect(server_url, session, element, session_http=session_http) == expected + + +@mark.asyncio +async def test_move_to_element(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//button" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + assert synchronous.actions_move_to_element(server_url, session, element) is True + async with ClientSession() as session_http: + assert ( + await asynchronous.actions_move_to_element(server_url, session, element, session_http=session_http) is True + ) + + +@mark.asyncio +async def test_actions_scroll_to_element(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//button" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + assert synchronous.actions_scroll_to_element(server_url, session, element) is True + async with ClientSession() as session_http: + assert ( + await asynchronous.actions_scroll_to_element(server_url, session, element, session_http=session_http) + is True + ) + + +@mark.asyncio +async def test_submit(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.NAME + locator_value = "my-form" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + assert synchronous.submit(server_url, session, element) is True + + synchronous.refresh_page(server_url, session) + element = synchronous.find_element(server_url, session, locator_type, locator_value) + async with ClientSession() as session_http: + assert await asynchronous.submit(server_url, session, element, session_http=session_http) is True + + +@mark.asyncio +async def test_actions_click(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//button" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + assert synchronous.actions_click(server_url, session, element) is True + async with ClientSession() as session_http: + assert await asynchronous.actions_click(server_url, session, element,session_http=session_http) is True + + +@mark.asyncio +async def test_raise_exception_when_element_not_found(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//invalid-tag" + + with raises(WebDriverError): + synchronous.find_element(server_url, session, locator_type, locator_value) + + with raises(WebDriverError): + async with ClientSession() as session_http: + await asynchronous.find_element( + server_url, session, locator_type, locator_value, session_http=session_http + ) + + +@mark.asyncio +async def test_set_timeouts(setup_functional_environment): + server_url, session = setup_functional_environment + timeouts_1 = 5000 # milliseconds + timeouts_2 = 3000 # milliseconds + + synchronous.set_timeouts(server_url, session, timeouts_1) + + assert synchronous.get_timeouts(server_url, session).get("implicit") == timeouts_1 + async with ClientSession() as session_http: + await asynchronous.set_timeouts(server_url, session, timeouts_2, session_http=session_http) + + assert synchronous.get_timeouts(server_url, session).get("implicit") == timeouts_2 + + +@mark.asyncio +async def test_find_children_elements(setup_functional_environment): + server_url, session = setup_functional_environment + expected = 1 # parent inclusive + locator_type = By.XPATH + locator_value = "//div" + + parent_element = synchronous.find_element( + server_url, session, locator_type, '//div[@class="parent"]' + ) + + children_elements = synchronous.find_children_elements( + server_url, session, parent_element, locator_type, locator_value + ) + + assert len(children_elements) > expected + async with ClientSession() as session_http: + children_elements = await asynchronous.find_children_elements( + server_url, session, parent_element, locator_type, locator_value, session_http=session_http + ) + + assert len(children_elements) > expected + + +@mark.asyncio +async def test_find_child_element(setup_functional_environment): + server_url, session = setup_functional_environment + expected = "any4" + locator_type = By.XPATH + locator_value = '//div[@class="child4"]' + + parent_element = synchronous.find_element( + server_url, session, locator_type, '//div[@class="parent"]' + ) + + child_element = synchronous.find_child_element( + server_url, session, parent_element, locator_type, locator_value + ) + + text = synchronous.get_text(server_url, session, child_element) + + assert text == expected + async with ClientSession() as session_http: + child_element = await asynchronous.find_child_element( + server_url, session, parent_element, locator_type, locator_value, session_http=session_http + ) + text = synchronous.get_text(server_url, session, child_element) + assert text == expected + + +@mark.asyncio +async def test_get_page_source(setup_functional_environment): + server_url, session = setup_functional_environment + expected = "Sample page" + + assert expected in synchronous.get_page_source(server_url, session) + async with ClientSession() as session_http: + assert expected in await asynchronous.get_page_source(server_url, session, session_http=session_http) + + +@mark.asyncio +async def test_execute_script_asynchronous(setup_functional_environment): + server_url, session = setup_functional_environment + script = "alert('any warn')" + async with ClientSession() as session_http: + assert await asynchronous.execute_script(server_url, session, script, session_http=session_http) is None + + +def test_execute_script_synchronous(setup_functional_environment): + server_url, session = setup_functional_environment + script = "alert('any warn')" + assert synchronous.execute_script(server_url, session, script) is None + + +@mark.asyncio +async def test_get_alert_text(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.CSS_SELECTOR + locator_value = "#alert-button" + expected = "any warn" + + alert_button = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + synchronous.click(server_url, session, alert_button) + + assert synchronous.get_alert_text(server_url, session) == expected + async with ClientSession() as session_http: + assert await asynchronous.get_alert_text(server_url, session, session_http=session_http) == expected + + +@mark.asyncio +async def test_get_active_element(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + synchronous.send_keys(server_url, session, element, "any") + + assert synchronous.get_active_element(server_url, session) == element + async with ClientSession() as session_http: + assert await asynchronous.get_active_element(server_url, session, session_http=session_http) == element + + +@mark.asyncio +async def test_clear_element_fails_when_invalid_inputs(setup_functional_environment): + server_url, session = setup_functional_environment + element = "invalid" + + with raises(WebDriverError): + synchronous.clear_element(server_url, session, element) is True + + with raises(WebDriverError): + async with ClientSession() as session_http: + await asynchronous.clear_element(server_url, session, element, session_http=session_http) + + +@mark.asyncio +async def test_clear_element(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + text = "any" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + synchronous.send_keys(server_url, session, element, text) + assert synchronous.clear_element(server_url, session, element) is True + + synchronous.send_keys(server_url, session, element, text) + async with ClientSession() as session_http: + assert await asynchronous.clear_element(server_url, session, element, session_http=session_http) is True + + +@mark.asyncio +async def test_is_element_enabled(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.is_element_enabled(server_url, session, element) is True + async with ClientSession() as session_http: + assert await asynchronous.is_element_enabled(server_url, session, element, session_http=session_http) is True + + +@mark.asyncio +async def test_get_css_value(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + property_name = "color" + expected = "rgba(0, 0, 0, 1)" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert ( + synchronous.get_css_value(server_url, session, element, property_name) + == expected + ) + async with ClientSession() as session_http: + assert ( + await asynchronous.get_css_value(server_url, session, element, property_name, session_http=session_http) + == expected + ) + + +@mark.asyncio +async def test_is_element_selected(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + assert synchronous.is_element_selected(server_url, session, element) is False + async with ClientSession() as session_http: + assert await asynchronous.is_element_selected(server_url, session, element, session_http=session_http) is False + + + +@mark.asyncio +async def test_get_window_rectangle(setup_functional_environment): + server_url, session = setup_functional_environment + expected = "height" + + assert expected in synchronous.get_window_rectangle(server_url, session) + async with ClientSession() as session_http: + rectangle = await asynchronous.get_window_rectangle(server_url, session, session_http=session_http) + assert expected in rectangle + + +@mark.asyncio +async def test_get_window_handles(setup_functional_environment): + server_url, session = setup_functional_environment + + assert isinstance(synchronous.get_window_handles(server_url, session), list) + async with ClientSession() as session_http: + handles = await asynchronous.get_window_handles(server_url, session, session_http=session_http) + assert isinstance(handles, list) + + +def test_close_window_sync(setup_functional_environment): + server_url, session = setup_functional_environment + assert isinstance(synchronous.close_window(server_url, session), list) + + +@mark.asyncio +async def test_close_window_async(setup_functional_environment): + server_url, session = setup_functional_environment + async with ClientSession() as session_http: + response = await asynchronous.close_window(server_url, session, session_http=session_http) + assert isinstance(response, list) + + +@mark.asyncio +async def test_get_window(setup_functional_environment): + server_url, session = setup_functional_environment + + assert synchronous.get_window(server_url, session) is not None + async with ClientSession() as session_http: + assert await asynchronous.get_window(server_url, session, session_http=session_http) is not None + + +@mark.asyncio +async def test_get_attribute_fails_when_invalid_attribute(setup_functional_environment): + server_url, session = setup_functional_environment + attribute = "href" + element = "invalid" + + with raises(WebDriverError): + synchronous.get_attribute(server_url, session, element, attribute) + + with raises(WebDriverError): + async with ClientSession() as session_http: + await asynchronous.get_attribute(server_url, session, element, attribute, session_http=session_http) + + +@mark.asyncio +async def test_get_attribute(setup_functional_environment): + server_url, session = setup_functional_environment + attribute = "href" + element = synchronous.find_element(server_url, session, By.XPATH, "//a[@id='a1']") + + assert ( + synchronous.get_attribute(server_url, session, element, attribute) + == "http://any1.com/" + ) + async with ClientSession() as session_http: + assert ( + await asynchronous.get_attribute(server_url, session, element, attribute, session_http=session_http) + == "http://any1.com/" + ) + + +@mark.asyncio +async def test_get_cookies(setup_functional_environment): + server_url, session = setup_functional_environment + assert isinstance(synchronous.get_cookies(server_url, session), list) + async with ClientSession() as session_http: + cookies = await asynchronous.get_cookies(server_url, session, session_http=session_http) + assert isinstance(cookies, list) + + +@mark.asyncio +async def test_go_back(setup_functional_environment): + server_url, session = setup_functional_environment + title = "" + + assert synchronous.go_back(server_url, session) is True + assert synchronous.get_title(server_url, session) == title + + synchronous.go_forward(server_url, session) + async with ClientSession() as session_http: + assert await asynchronous.go_back(server_url, session, session_http=session_http) is True + assert synchronous.get_title(server_url, session) == title + + +@mark.asyncio +async def test_get_url(setup_functional_environment): + server_url, session = setup_functional_environment + expected = "playground.html" + + assert expected in synchronous.get_url(server_url, session) + async with ClientSession() as session_http: + assert expected in await asynchronous.get_url(server_url, session, session_http=session_http) + + +@mark.asyncio +async def test_get_timeouts(setup_functional_environment): + server_url, session = setup_functional_environment + expected = "implicit" + + assert expected in synchronous.get_timeouts(server_url, session) + async with ClientSession() as session_http: + assert expected in await asynchronous.get_timeouts(server_url, session, session_http=session_http) + + +@mark.asyncio +async def test_get_status(setup_functional_environment): + server_url, _ = setup_functional_environment + expected = "ready" + assert expected in synchronous.get_status(server_url).get("value") + async with ClientSession() as session_http: + response = await asynchronous.get_status(server_url, session_http=session_http) + assert expected in response.get("value") + + +@mark.asyncio +async def test_get_title(setup_functional_environment): + server_url, session = setup_functional_environment + expected = "Sample page" + + assert synchronous.get_title(server_url, session) == expected + async with ClientSession() as session_http: + assert await asynchronous.get_title(server_url, session, session_http=session_http) == expected + + +@mark.asyncio +async def test_find_elements_fails_when_invalid_data_input( + setup_functional_environment, +): + server_url, session = setup_functional_environment + locator_type = "invalid" + locator_value = "//input" + + with raises(WebDriverError): + synchronous.find_elements(server_url, session, locator_type, locator_value) + + with raises(WebDriverError): + async with ClientSession() as session_http: + await asynchronous.find_elements( + server_url, session, locator_type, locator_value, session_http=session_http + ) + + +@mark.asyncio +async def test_find_elements(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + + elements = synchronous.find_elements( + server_url, session, locator_type, locator_value + ) + async with ClientSession() as session_http: + async_elements = await asynchronous.find_elements( + server_url, session, locator_type, locator_value, session_http=session_http + ) + + assert len(elements) > 0 + assert len(async_elements) > 0 + + +@mark.asyncio +async def test_find_element_fails_when_invalid_data_input(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = "invalid" + locator_value = "//input" + + with raises(WebDriverError): + synchronous.find_element(server_url, session, locator_type, locator_value) + + with raises(WebDriverError): + async with ClientSession() as session_http: + await asynchronous.find_element( + server_url, session, locator_type, locator_value, session_http=session_http + ) + + +@mark.asyncio +async def test_find_element(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//input" + + assert ( + synchronous.find_element(server_url, session, locator_type, locator_value) + is not None + ) + async with ClientSession() as session_http: + assert ( + await asynchronous.find_element( + server_url, session, locator_type, locator_value, session_http=session_http + ) + is not None + ) + + +@mark.asyncio +async def test_get_property(setup_functional_environment): + server_url, session = setup_functional_environment + text = "any_value" + locator_type = By.XPATH + locator_value = "//input" + property = "value" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + synchronous.send_keys(server_url, session, element, text) + + assert synchronous.get_property(server_url, session, element, property) == text + async with ClientSession() as session_http: + assert ( + await asynchronous.get_property( + server_url, session, element, property, session_http=session_http + ) + == text + ) + + +@mark.asyncio +async def test_get_text(setup_functional_environment): + server_url, session = setup_functional_environment + expected = "end" + locator_type = By.XPATH + locator_value = "//p[@id='end']" #

end

+ + element = synchronous.find_element(server_url, session, locator_type, locator_value) + async with ClientSession() as session_http: + assert ( + await asynchronous.get_text( + server_url, session, element, session_http=session_http + ) + == expected + ) + assert synchronous.get_text(server_url, session, element) == expected + + +@mark.asyncio +async def test_send_keys(setup_functional_environment): + server_url, session = setup_functional_environment + text_async = "any_async" + text_sync = "any_sync" + locator_type = By.XPATH + locator_value = "//input" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + + async with ClientSession() as session_http: + assert ( + await asynchronous.send_keys( + server_url, session, element, text_async, session_http=session_http + ) + is True + ) + assert synchronous.send_keys(server_url, session, element, text_sync) is True + + +@mark.asyncio +async def test_click(setup_functional_environment): + server_url, session = setup_functional_environment + locator_type = By.XPATH + locator_value = "//button" + + element = synchronous.find_element(server_url, session, locator_type, locator_value) + async with ClientSession() as session_http: + assert ( + await asynchronous.click( + server_url, session, element, session_http=session_http + ) + is True + ) + assert synchronous.click(server_url, session, element) is True diff --git a/tests/feature/test_sync_and_async.py b/tests/feature/test_sync_and_async.py index 8bb6cc6..b02cee9 100644 --- a/tests/feature/test_sync_and_async.py +++ b/tests/feature/test_sync_and_async.py @@ -1,3 +1,4 @@ +import aiohttp from pytest import mark, raises from caqui import asynchronous, synchronous from caqui.exceptions import WebDriverError @@ -107,7 +108,10 @@ async def test_set_window_rectangle(setup_functional_environment): x = window_rectangle_before.get("x") + 1 y = window_rectangle_before.get("y") + 1 - assert synchronous.set_window_rectangle(server_url, session, width, height, x, y) is True + assert ( + synchronous.set_window_rectangle(server_url, session, width, height, x, y) + is True + ) window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before @@ -118,7 +122,12 @@ async def test_set_window_rectangle(setup_functional_environment): synchronous.maximize_window(server_url, session) - assert await asynchronous.set_window_rectangle(server_url, session, width, height, x, y) is True + assert ( + await asynchronous.set_window_rectangle( + server_url, session, width, height, x, y + ) + is True + ) window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) @@ -219,7 +228,10 @@ async def test_switch_to_window(setup_functional_environment, window_type): assert synchronous.get_title(server_url, session) == "" synchronous.switch_to_window(server_url, session, handle=sample_page) is True - assert await asynchronous.switch_to_window(server_url, session, handle=new_page) is True + assert ( + await asynchronous.switch_to_window(server_url, session, handle=new_page) + is True + ) assert synchronous.get_title(server_url, session) == "" @@ -241,8 +253,13 @@ async def test_switch_to_parent_frame_asynchronous(setup_functional_environment) locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) - assert await asynchronous.switch_to_parent_frame(server_url, session, element_frame) is True + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + assert ( + await asynchronous.switch_to_parent_frame(server_url, session, element_frame) + is True + ) def test_switch_to_parent_frame_synchronous(setup_functional_environment): @@ -250,8 +267,12 @@ def test_switch_to_parent_frame_synchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) - assert synchronous.switch_to_parent_frame(server_url, session, element_frame) is True + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + assert ( + synchronous.switch_to_parent_frame(server_url, session, element_frame) is True + ) @mark.asyncio @@ -260,8 +281,12 @@ async def test_switch_to_frame_asynchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) - assert await asynchronous.switch_to_frame(server_url, session, element_frame) is True + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) + assert ( + await asynchronous.switch_to_frame(server_url, session, element_frame) is True + ) def test_switch_to_frame_synchronous(setup_functional_environment): @@ -269,7 +294,9 @@ def test_switch_to_frame_synchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) + element_frame = synchronous.find_element( + server_url, session, locator_type, locator_value + ) assert synchronous.switch_to_frame(server_url, session, element_frame) is True @@ -329,7 +356,13 @@ async def test_take_screenshot_element(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.take_screenshot_element(server_url, session, element) is True - assert await asynchronous.take_screenshot_element(server_url, session, element) is True + async with aiohttp.ClientSession() as session_http: + assert ( + await asynchronous.take_screenshot_element( + server_url, session, element, session_http=session_http + ) + is True + ) @mark.asyncio @@ -374,7 +407,9 @@ async def test_get_named_cookie(setup_functional_environment): name = "username" # cookie created on page load expected = "John Doe" - assert synchronous.get_named_cookie(server_url, session, name).get("value") == expected + assert ( + synchronous.get_named_cookie(server_url, session, name).get("value") == expected + ) response = await asynchronous.get_named_cookie(server_url, session, name) assert response.get("value") == expected @@ -390,7 +425,9 @@ async def test_get_computed_label(setup_functional_environment): assert synchronous.get_computed_label(server_url, session, element) == expected - assert await asynchronous.get_computed_label(server_url, session, element) == expected + assert ( + await asynchronous.get_computed_label(server_url, session, element) == expected + ) @mark.asyncio @@ -404,7 +441,9 @@ async def test_get_computed_role(setup_functional_environment): assert synchronous.get_computed_role(server_url, session, element) == expected - assert await asynchronous.get_computed_role(server_url, session, element) == expected + assert ( + await asynchronous.get_computed_role(server_url, session, element) == expected + ) @mark.asyncio @@ -421,9 +460,13 @@ async def test_get_tag_name(setup_functional_environment): assert await asynchronous.get_tag_name(server_url, session, element) == expected -@mark.parametrize("locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")]) +@mark.parametrize( + "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] +) @mark.asyncio -async def test_find_element_from_shadow_root(setup_functional_environment, locator, value): +async def test_find_element_from_shadow_root( + setup_functional_environment, locator, value +): server_url, session = setup_functional_environment locator_type = By.ID locator_value = "shadow-root" @@ -432,18 +475,26 @@ async def test_find_element_from_shadow_root(setup_functional_environment, locat shadow_root = synchronous.get_shadow_root(server_url, session, element) - actual = synchronous.find_child_element(server_url, session, shadow_root, locator, value) + actual = synchronous.find_child_element( + server_url, session, shadow_root, locator, value + ) assert actual is not None - actual = await asynchronous.find_child_element(server_url, session, shadow_root, locator, value) + actual = await asynchronous.find_child_element( + server_url, session, shadow_root, locator, value + ) assert actual is not None -@mark.parametrize("locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")]) +@mark.parametrize( + "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] +) @mark.asyncio -async def test_find_elements_from_shadow_root(setup_functional_environment, locator, value): +async def test_find_elements_from_shadow_root( + setup_functional_environment, locator, value +): server_url, session = setup_functional_environment locator_type = By.ID locator_value = "shadow-root" @@ -453,7 +504,9 @@ async def test_find_elements_from_shadow_root(setup_functional_environment, loca shadow_root = synchronous.get_shadow_root(server_url, session, element) - actual = synchronous.find_children_elements(server_url, session, shadow_root, locator, value) + actual = synchronous.find_children_elements( + server_url, session, shadow_root, locator, value + ) assert len(actual) == one @@ -500,7 +553,9 @@ async def test_move_to_element(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.actions_move_to_element(server_url, session, element) is True - assert await asynchronous.actions_move_to_element(server_url, session, element) is True + assert ( + await asynchronous.actions_move_to_element(server_url, session, element) is True + ) @mark.asyncio @@ -511,7 +566,10 @@ async def test_actions_scroll_to_element(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.actions_scroll_to_element(server_url, session, element) is True - assert await asynchronous.actions_scroll_to_element(server_url, session, element) is True + assert ( + await asynchronous.actions_scroll_to_element(server_url, session, element) + is True + ) @mark.asyncio @@ -549,7 +607,9 @@ async def test_raise_exception_when_element_not_found(setup_functional_environme synchronous.find_element(server_url, session, locator_type, locator_value) with raises(WebDriverError): - await asynchronous.find_element(server_url, session, locator_type, locator_value) + await asynchronous.find_element( + server_url, session, locator_type, locator_value + ) @mark.asyncio @@ -647,7 +707,9 @@ async def test_get_alert_text(setup_functional_environment): locator_value = "#alert-button" expected = "any warn" - alert_button = synchronous.find_element(server_url, session, locator_type, locator_value) + alert_button = synchronous.find_element( + server_url, session, locator_type, locator_value + ) synchronous.click(server_url, session, alert_button) assert synchronous.get_alert_text(server_url, session) == expected @@ -716,8 +778,14 @@ async def test_get_css_value(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) - assert synchronous.get_css_value(server_url, session, element, property_name) == expected - assert await asynchronous.get_css_value(server_url, session, element, property_name) == expected + assert ( + synchronous.get_css_value(server_url, session, element, property_name) + == expected + ) + assert ( + await asynchronous.get_css_value(server_url, session, element, property_name) + == expected + ) @mark.asyncio @@ -791,7 +859,10 @@ async def test_get_attribute(setup_functional_environment): attribute = "href" element = synchronous.find_element(server_url, session, By.XPATH, "//a[@id='a1']") - assert synchronous.get_attribute(server_url, session, element, attribute) == "http://any1.com/" + assert ( + synchronous.get_attribute(server_url, session, element, attribute) + == "http://any1.com/" + ) assert ( await asynchronous.get_attribute(server_url, session, element, attribute) == "http://any1.com/" @@ -856,7 +927,9 @@ async def test_get_title(setup_functional_environment): @mark.asyncio -async def test_find_elements_fails_when_invalid_data_input(setup_functional_environment): +async def test_find_elements_fails_when_invalid_data_input( + setup_functional_environment, +): server_url, session = setup_functional_environment locator_type = "invalid" locator_value = "//input" @@ -865,7 +938,9 @@ async def test_find_elements_fails_when_invalid_data_input(setup_functional_envi synchronous.find_elements(server_url, session, locator_type, locator_value) with raises(WebDriverError): - await asynchronous.find_elements(server_url, session, locator_type, locator_value) + await asynchronous.find_elements( + server_url, session, locator_type, locator_value + ) @mark.asyncio @@ -874,7 +949,9 @@ async def test_find_elements(setup_functional_environment): locator_type = By.XPATH locator_value = "//input" - elements = synchronous.find_elements(server_url, session, locator_type, locator_value) + elements = synchronous.find_elements( + server_url, session, locator_type, locator_value + ) async_elements = await asynchronous.find_elements( server_url, session, locator_type, locator_value ) @@ -893,7 +970,9 @@ async def test_find_element_fails_when_invalid_data_input(setup_functional_envir synchronous.find_element(server_url, session, locator_type, locator_value) with raises(WebDriverError): - await asynchronous.find_element(server_url, session, locator_type, locator_value) + await asynchronous.find_element( + server_url, session, locator_type, locator_value + ) @mark.asyncio @@ -902,9 +981,14 @@ async def test_find_element(setup_functional_environment): locator_type = By.XPATH locator_value = "//input" - assert synchronous.find_element(server_url, session, locator_type, locator_value) is not None assert ( - await asynchronous.find_element(server_url, session, locator_type, locator_value) + synchronous.find_element(server_url, session, locator_type, locator_value) + is not None + ) + assert ( + await asynchronous.find_element( + server_url, session, locator_type, locator_value + ) is not None ) @@ -921,7 +1005,9 @@ async def test_get_property(setup_functional_environment): synchronous.send_keys(server_url, session, element, text) assert synchronous.get_property(server_url, session, element, property) == text - assert await asynchronous.get_property(server_url, session, element, property) == text + assert ( + await asynchronous.get_property(server_url, session, element, property) == text + ) @mark.asyncio @@ -947,7 +1033,9 @@ async def test_send_keys(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) - assert await asynchronous.send_keys(server_url, session, element, text_async) is True + assert ( + await asynchronous.send_keys(server_url, session, element, text_async) is True + ) assert synchronous.send_keys(server_url, session, element, text_sync) is True diff --git a/tests/integration/test_async_scenarios.py b/tests/integration/test_async_scenarios.py index f3dc302..d9ba381 100644 --- a/tests/integration/test_async_scenarios.py +++ b/tests/integration/test_async_scenarios.py @@ -2,6 +2,7 @@ from pytest import mark + @mark.asyncio async def test_get_all_links(setup_functional_environment): server_url, session = setup_functional_environment @@ -11,6 +12,11 @@ async def test_get_all_links(setup_functional_environment): for i in range(4): i += 1 locator_value = f"//a[@id='a{i}']" - anchor = synchronous.find_element(server_url, session, locator_type, locator_value) + anchor = synchronous.find_element( + server_url, session, locator_type, locator_value + ) anchors.append(anchor) - assert await asynchronous.get_text(server_url, session, anchors[i - 1]) == f"any{i}.com" + assert ( + await asynchronous.get_text(server_url, session, anchors[i - 1]) + == f"any{i}.com" + ) diff --git a/tests/unit/test_async_unit.py b/tests/unit/test_async_unit.py index 3ee71c8..daafc89 100644 --- a/tests/unit/test_async_unit.py +++ b/tests/unit/test_async_unit.py @@ -7,7 +7,7 @@ from unittest.mock import patch -async def mock_request(*args): +async def mock_request(*args, **kwargs): pass @@ -15,7 +15,7 @@ async def mock_request(*args): async def test_get_rect(): expected = {"height": 23, "width": 183, "x": 10, "y": 9652.12} - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_RECT with patch("caqui.asynchronous.__get", mock_request): @@ -24,7 +24,7 @@ async def mock_request(*args): @mark.asyncio async def test_actions_scroll_to_element(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.ACTIONS with patch("caqui.asynchronous.__post", mock_request): @@ -33,7 +33,7 @@ async def mock_request(*args): @mark.asyncio async def test_submit(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.CLICK with patch("caqui.asynchronous.__post", mock_request): @@ -42,7 +42,7 @@ async def mock_request(*args): @mark.asyncio async def test_actions_click(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.ACTIONS with patch("caqui.asynchronous.__post", mock_request): @@ -51,7 +51,7 @@ async def mock_request(*args): @mark.asyncio async def test_set_timeouts(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_TIMEOUTS with patch("caqui.asynchronous.__post", mock_request): @@ -62,7 +62,7 @@ async def mock_request(*args): async def test_find_children_elements(): element = "C230605181E69CB2C4C36B8E83FE1245_element_2" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENTS with patch("caqui.asynchronous.__post", mock_request): @@ -73,7 +73,7 @@ async def mock_request(*args): async def test_find_child_element(): element = "0.8851292311864847-1" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENT with patch("caqui.asynchronous.__post", mock_request): @@ -84,7 +84,7 @@ async def mock_request(*args): async def test_execute_script(): expected = "any" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.EXECUTE_SCRIPT with patch("caqui.asynchronous.__post", mock_request): @@ -95,7 +95,7 @@ async def mock_request(*args): async def test_get_page_source(): expected = "Sample page" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_PAGE_SOURCE with patch("caqui.asynchronous.__get", mock_request): @@ -106,7 +106,7 @@ async def mock_request(*args): async def test_get_alert_text(): expected = "any warn" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_ALERT_TEXT with patch("caqui.asynchronous.__get", mock_request): @@ -117,7 +117,7 @@ async def mock_request(*args): async def test_get_active_element(): expected = "0.8851292311864847-1" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_ACTIVE_ELEMENT with patch("caqui.asynchronous.__get", mock_request): @@ -126,7 +126,7 @@ async def mock_request(*args): @mark.asyncio async def test_clear_element(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.CLEAR_ELEMENT with patch("caqui.asynchronous.__post", mock_request): @@ -135,7 +135,7 @@ async def mock_request(*args): @mark.asyncio async def test_is_element_enabled(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.IS_ELEMENT_ENABLED with patch("caqui.asynchronous.__get", mock_request): @@ -146,7 +146,7 @@ async def mock_request(*args): async def test_get_css_value(): expected = "rgba(0, 0, 0, 1)" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_CSS_COLOR_VALUE with patch("caqui.asynchronous.__get", mock_request): @@ -155,7 +155,7 @@ async def mock_request(*args): @mark.asyncio async def test_is_element_selected(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.IS_ELEMENT_SELECTED with patch("caqui.asynchronous.__get", mock_request): @@ -166,7 +166,7 @@ async def mock_request(*args): async def test_get_window_rectangle(): expected = "height" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_WINDOW_RECTANGLE with patch("caqui.asynchronous.__get", mock_request): @@ -177,7 +177,7 @@ async def mock_request(*args): async def test_get_window_handles(): expected = ["2E55CCE389196328988ED244DAA52A5D"] - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_WINDOW_HANDLES with patch("caqui.asynchronous.__get", mock_request): @@ -188,7 +188,7 @@ async def mock_request(*args): async def test_close_window(): expected = [] - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.CLOSE_WINDOW with patch("caqui.asynchronous.__delete", mock_request): @@ -199,7 +199,7 @@ async def mock_request(*args): async def test_get_window(): expected = "845623CAE8115F2B60C9AE8596F13D94" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_WINDOW with patch("caqui.asynchronous.__get", mock_request): @@ -216,7 +216,7 @@ async def test_go_back(): async def test_get_property(): expected = "any_value" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_PROPERTY_VALUE with patch("caqui.asynchronous.__get", mock_request): @@ -227,7 +227,7 @@ async def mock_request(*args): async def test_get_attribute(): expected = "any_value" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_ATTRIBUTE_VALUE with patch("caqui.asynchronous.__get", mock_request): @@ -238,7 +238,7 @@ async def mock_request(*args): async def test_get_url(): expected = "playground.html" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_URL with patch("caqui.asynchronous.__get", mock_request): @@ -250,7 +250,7 @@ async def mock_request(*args): async def test_get_timeouts(): expected = "implicit" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_TIMEOUTS with patch("caqui.asynchronous.__get", mock_request): @@ -260,7 +260,7 @@ async def mock_request(*args): @mark.asyncio async def test_get_status(): - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_STATUS with patch("caqui.asynchronous.__get", mock_request): @@ -272,7 +272,7 @@ async def mock_request(*args): async def test_get_title(): expected = "Sample page" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_TITLE with patch("caqui.asynchronous.__get", mock_request): @@ -283,7 +283,7 @@ async def mock_request(*args): async def test_get_cookies(): expected = [] - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_COOKIES with patch("caqui.asynchronous.__get", mock_request): @@ -294,7 +294,7 @@ async def mock_request(*args): async def test_get_text(): expected = "any" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_TEXT with patch("caqui.asynchronous.__get", mock_request): @@ -329,7 +329,7 @@ async def test_click(): async def test_find_elements(): element = "C230605181E69CB2C4C36B8E83FE1245_element_2" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENTS with patch("caqui.asynchronous.__post", mock_request): @@ -340,7 +340,7 @@ async def mock_request(*args): async def test_find_element(): element = "0.8851292311864847-1" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENT with patch("caqui.asynchronous.__post", mock_request): @@ -351,7 +351,7 @@ async def mock_request(*args): async def test_get_session(): expected = "4358a5b53794586af59678fc1653dc40" - async def mock_request(*args): + async def mock_request(*args, **kwargs): return fake_responses.GET_SESSION with patch("caqui.asynchronous.__post", mock_request): From 1d7f417515ebd82ab7ef76d22f0230d6ff41641d Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Sat, 29 Nov 2025 02:46:10 -0300 Subject: [PATCH 03/40] add performance tests --- caqui/asynchronous.py | 126 +-- caqui/easy/action_chains.py | 4 +- caqui/easy/alert.py | 12 +- caqui/easy/element.py | 77 +- caqui/easy/page.py | 102 ++- caqui/easy/server.py | 41 +- caqui/easy/switch_to.py | 10 +- caqui/synchronous.py | 416 ++++----- dev-requirements.txt | 3 +- samples/sample-web-driver.py | 4 +- tests/conftest.py | 11 +- tests/feature/test_async_with_http_session.py | 365 ++++---- tests/feature/test_sync_and_async.py | 147 +-- tests/integration/test_async_scenarios.py | 10 +- tests/performance/README.md | 858 ++++++++++++++++++ tests/performance/test_process_data.py | 463 ++++++++++ tests/performance/test_single_session_http.py | 278 ++++++ tests/unit/test_async_unit.py | 70 +- tests/unit/test_sync_unit.py | 68 +- 19 files changed, 2301 insertions(+), 764 deletions(-) create mode 100644 tests/performance/README.md create mode 100644 tests/performance/test_process_data.py create mode 100644 tests/performance/test_single_session_http.py diff --git a/caqui/asynchronous.py b/caqui/asynchronous.py index 05f29f0..7b6b4a6 100644 --- a/caqui/asynchronous.py +++ b/caqui/asynchronous.py @@ -1,7 +1,7 @@ from aiohttp import ClientSession -from json import dumps -from caqui.constants import HEADERS as __HEADERS -from caqui.exceptions import WebDriverError as WebDriverError +from orjson import dumps +from caqui.constants import HEADERS +from caqui.exceptions import WebDriverError from caqui.helper import save_picture, get_elements, get_element from typing import Optional @@ -24,7 +24,7 @@ async def _handle_response(resp): async def _delete(url, session_http: ClientSession = None): if session_http: try: - async with session_http.delete(url, headers=__HEADERS) as resp: + async with session_http.delete(url, headers=HEADERS) as resp: return await _handle_response(resp) except Exception as e: raise WebDriverError("'DELETE' request failed.") from e @@ -32,7 +32,7 @@ async def _delete(url, session_http: ClientSession = None): else: try: async with ClientSession() as session_http: - async with session_http.delete(url, headers=__HEADERS) as resp: + async with session_http.delete(url, headers=HEADERS) as resp: return await _handle_response(resp) except Exception as e: raise WebDriverError("'DELETE' request failed.") from e @@ -41,18 +41,14 @@ async def _delete(url, session_http: ClientSession = None): async def _post(url, payload, session_http: ClientSession = None): if session_http: try: - async with session_http.post( - url, data=dumps(payload), headers=__HEADERS - ) as resp: + async with session_http.post(url, data=dumps(payload), headers=HEADERS) as resp: return await _handle_response(resp) except Exception as e: raise WebDriverError("'POST' request failed.") from e else: try: async with ClientSession() as session_http: - async with session_http.post( - url, data=dumps(payload), headers=__HEADERS - ) as resp: + async with session_http.post(url, data=dumps(payload), headers=HEADERS) as resp: return await _handle_response(resp) except Exception as e: raise WebDriverError("'POST' request failed.") from e @@ -61,14 +57,14 @@ async def _post(url, payload, session_http: ClientSession = None): async def _get(url, session_http: ClientSession = None): if session_http: try: - async with session_http.get(url, headers=__HEADERS) as resp: + async with session_http.get(url, headers=HEADERS) as resp: return await _handle_response(resp) except Exception as e: raise WebDriverError("'GET' request failed.") from e else: try: async with ClientSession() as session_http: - async with session_http.get(url, headers=__HEADERS) as resp: + async with session_http.get(url, headers=HEADERS) as resp: return await _handle_response(resp) except Exception as e: raise WebDriverError("'GET' request failed.") from e @@ -83,9 +79,7 @@ async def _handle_alert(server_url, session, command, session_http) -> bool: return True -async def _handle_window( - server_url, session, command, session_http: ClientSession = None -): +async def _handle_window(server_url, session, command, session_http: ClientSession = None): url = f"{server_url}/session/{session}/window/{command}" payload = {} await _post(url, payload, session_http=session_http) @@ -135,9 +129,7 @@ async def go_forward(server_url, session, session_http: ClientSession = None): raise WebDriverError("Failed to go to page forward.") from e -async def set_window_rectangle( - server_url, session, width, height, x, y, session_http=None -): +async def set_window_rectangle(server_url, session, width, height, x, y, session_http=None): """Set window rectangle""" try: url = f"{server_url}/session/{session}/window/rect" @@ -178,9 +170,7 @@ async def maximize_window(server_url, session, session_http: ClientSession = Non raise WebDriverError("Failed to maximize window.") from e -async def switch_to_window( - server_url, session, handle, session_http: ClientSession = None -): +async def switch_to_window(server_url, session, handle, session_http: ClientSession = None): """Switch to window""" try: url = f"{server_url}/session/{session}/window" @@ -221,9 +211,7 @@ async def switch_to_parent_frame( raise WebDriverError("Failed to switch to parent frame.") from e -async def switch_to_frame( - server_url, session, element_frame, session_http: ClientSession = None -): +async def switch_to_frame(server_url, session, element_frame, session_http: ClientSession = None): """Switch to frame 'element_frame'""" try: url = f"{server_url}/session/{session}/frame" @@ -244,9 +232,7 @@ async def delete_all_cookies(server_url, session, session_http: ClientSession = raise WebDriverError("Failed to delete cookies.") from e -async def send_alert_text( - server_url, session, text, session_http: ClientSession = None -): +async def send_alert_text(server_url, session, text, session_http: ClientSession = None): """Fill the alert text area and send the text""" try: url = f"{server_url}/session/{session}/alert/text" @@ -262,9 +248,7 @@ async def send_alert_text( async def accept_alert(server_url, session, session_http: ClientSession = None): """Accept alert""" try: - return await _handle_alert( - server_url, session, "accept", session_http=session_http - ) + return await _handle_alert(server_url, session, "accept", session_http=session_http) except Exception as e: raise WebDriverError("Failed to accept alert.") from e @@ -272,9 +256,7 @@ async def accept_alert(server_url, session, session_http: ClientSession = None): async def dismiss_alert(server_url, session, session_http: ClientSession = None): """Dismiss alert""" try: - return await _handle_alert( - server_url, session, "dismiss", session_http=session_http - ) + return await _handle_alert(server_url, session, "dismiss", session_http=session_http) except Exception as e: raise WebDriverError("Failed to dismiss alert.") from e @@ -316,9 +298,7 @@ async def take_screenshot( raise WebDriverError("Failed to take screenshot.") from e -async def get_named_cookie( - server_url, session, name, session_http: ClientSession = None -) -> str: +async def get_named_cookie(server_url, session, name, session_http: ClientSession = None) -> str: """Get cookie by name""" try: url = f"{server_url}/session/{session}/cookie/{name}" @@ -352,9 +332,7 @@ async def get_computed_role( raise WebDriverError("Failed to get element computed role.") from e -async def get_tag_name( - server_url, session, element, session_http: ClientSession = None -) -> str: +async def get_tag_name(server_url, session, element, session_http: ClientSession = None) -> str: """Get the element tag name""" try: url = f"{server_url}/session/{session}/element/{element}/name" @@ -364,9 +342,7 @@ async def get_tag_name( raise WebDriverError("Failed to get element name.") from e -async def get_shadow_root( - server_url, session, element, session_http: ClientSession = None -) -> dict: +async def get_shadow_root(server_url, session, element, session_http: ClientSession = None) -> dict: """Get the shadow root element""" try: root_element = "shadow-6066-11e4-a52e-4f735466cecf" @@ -377,9 +353,7 @@ async def get_shadow_root( raise WebDriverError("Failed to get element shadow.") from e -async def get_rect( - server_url, session, element, session_http: ClientSession = None -) -> dict: +async def get_rect(server_url, session, element, session_http: ClientSession = None) -> dict: """Get the element rectangle""" try: url = f"{server_url}/session/{session}/element/{element}/rect" @@ -395,9 +369,7 @@ async def actions(server_url, session, payload, session_http: ClientSession = No return True -async def actions_move_to_element( - server_url, session, element, session_http: ClientSession = None -): +async def actions_move_to_element(server_url, session, element, session_http: ClientSession = None): """Move to an element simulating a mouse movement""" try: payload = { @@ -468,18 +440,14 @@ async def submit(server_url, session, element, session_http: ClientSession = Non element, locator_type="xpath", locator_value="*[@type='submit']", - session_http=session_http - ) - return await click( - server_url, session, submit_element, session_http=session_http + session_http=session_http, ) + return await click(server_url, session, submit_element, session_http=session_http) except Exception as e: raise WebDriverError("Failed to submit form.") from e -async def actions_click( - server_url, session, element, session_http: ClientSession = None -): +async def actions_click(server_url, session, element, session_http: ClientSession = None): """Click an element simulating a mouse movement""" try: payload = { @@ -516,9 +484,7 @@ async def actions_click( raise WebDriverError("Failed to click the element.") from e -async def set_timeouts( - server_url, session, timeouts, session_http: ClientSession = None -): +async def set_timeouts(server_url, session, timeouts, session_http: ClientSession = None): """Set timeouts""" try: url = f"{server_url}/session/{session}/timeouts" @@ -570,14 +536,10 @@ async def find_child_element( response = await _post(url, payload, session_http=session_http) return get_element(response) except Exception as e: - raise WebDriverError( - f"Failed to find the child element from '{parent_element}'." - ) from e + raise WebDriverError(f"Failed to find the child element from '{parent_element}'.") from e -async def get_page_source( - server_url, session, session_http: ClientSession = None -) -> str: +async def get_page_source(server_url, session, session_http: ClientSession = None) -> str: """Get the page source (all content)""" try: url = f"{server_url}/session/{session}/source" @@ -587,9 +549,7 @@ async def get_page_source( raise WebDriverError("Failed to get the page source.") from e -async def execute_script( - server_url, session, script, args=[], session_http: ClientSession = None -): +async def execute_script(server_url, session, script, args=[], session_http: ClientSession = None): """Executes a script, like 'alert('something')' to open an alert window""" try: url = f"{server_url}/session/{session}/execute/sync" @@ -600,9 +560,7 @@ async def execute_script( raise WebDriverError("Failed to execute script.") from e -async def get_alert_text( - server_url, session, session_http: ClientSession = None -) -> str: +async def get_alert_text(server_url, session, session_http: ClientSession = None) -> str: """Get the text from an alert""" try: url = f"{server_url}/session/{session}/alert/text" @@ -622,9 +580,7 @@ async def get_active_element(server_url, session, session_http: ClientSession = raise WebDriverError("Failed to check if element is selected.") from e -async def clear_element( - server_url, session, element, session_http: ClientSession = None -): +async def clear_element(server_url, session, element, session_http: ClientSession = None): """Clear the element text""" try: url = f"{server_url}/session/{session}/element/{element}/clear" @@ -671,9 +627,7 @@ async def is_element_selected( raise WebDriverError("Failed to check if element is selected.") from e -async def get_window_rectangle( - server_url, session, session_http: ClientSession = None -) -> dict: +async def get_window_rectangle(server_url, session, session_http: ClientSession = None) -> dict: """Get window rectangle""" try: url = f"{server_url}/session/{session}/window/rect" @@ -683,9 +637,7 @@ async def get_window_rectangle( raise WebDriverError("Failed to get window rectangle.") from e -async def get_window_handles( - server_url, session, session_http: ClientSession = None -) -> list: +async def get_window_handles(server_url, session, session_http: ClientSession = None) -> list: """Get window handles""" try: url = f"{server_url}/session/{session}/window/handles" @@ -811,9 +763,7 @@ async def get_attribute( raise WebDriverError("Failed to get value from element.") from e -async def get_text( - server_url, session, element, session_http: ClientSession = None -) -> str: +async def get_text(server_url, session, element, session_http: ClientSession = None) -> str: """Get the text of an element""" try: url = f"{server_url}/session/{session}/element/{element}/text" @@ -848,7 +798,7 @@ async def get(server_url, session, page_url, session_http: ClientSession = None) return go_to_page(server_url, session, page_url, session_http=session_http) -async def go_to_page(server_url, session, page_url, session_http:ClientSession=None): +async def go_to_page(server_url, session, page_url, session_http: ClientSession = None): """Navigate to 'page_url'""" try: url = f"{server_url}/session/{session}/url" @@ -859,9 +809,7 @@ async def go_to_page(server_url, session, page_url, session_http:ClientSession=N raise WebDriverError(f"Failed to navigate to page '{page_url}'.") from e -async def send_keys( - server_url, session, element, text, session_http: ClientSession = None -): +async def send_keys(server_url, session, element, text, session_http: ClientSession = None): """Fill an editable element, for example a textarea, with a given text""" try: url = f"{server_url}/session/{session}/element/{element}/value" @@ -920,6 +868,4 @@ async def get_session( response = await _post(url, capabilities, session_http=session_http) return response.get("sessionId") except Exception as e: - raise WebDriverError( - "Failed to open session. Check the browser capabilities." - ) from e + raise WebDriverError("Failed to open session. Check the browser capabilities.") from e diff --git a/caqui/easy/action_chains.py b/caqui/easy/action_chains.py index e278c5f..cc7181e 100644 --- a/caqui/easy/action_chains.py +++ b/caqui/easy/action_chains.py @@ -16,7 +16,9 @@ def click(self, element: Element): Clicks on the element `element` """ self._element = element - coroutine = asynchronous.click(self._remote, self._session, str(element), session_http=self._session_http) + coroutine = asynchronous.click( + self._remote, self._session, str(element), session_http=self._session_http + ) self._coroutines.append(coroutine) return self diff --git a/caqui/easy/alert.py b/caqui/easy/alert.py index d1ba6f7..bb42291 100644 --- a/caqui/easy/alert.py +++ b/caqui/easy/alert.py @@ -14,12 +14,18 @@ def text(self): async def accept(self): """Accepts the alert""" - return await asynchronous.accept_alert(self._remote, self._session, session_http=self._session_http) + return await asynchronous.accept_alert( + self._remote, self._session, session_http=self._session_http + ) async def dismiss(self): """Closes the alert ignoring it""" - return await asynchronous.dismiss_alert(self._remote, self._session, session_http=self._session_http) + return await asynchronous.dismiss_alert( + self._remote, self._session, session_http=self._session_http + ) async def send_keys(self, text): """Send a text to a textbox in the alert""" - return await asynchronous.send_alert_text(self._remote, self._session, text, session_http=self._session_http) + return await asynchronous.send_alert_text( + self._remote, self._session, text, session_http=self._session_http + ) diff --git a/caqui/easy/element.py b/caqui/easy/element.py index 6ade2fa..fd96643 100644 --- a/caqui/easy/element.py +++ b/caqui/easy/element.py @@ -39,7 +39,11 @@ def active_element(self): async def value_of_css_property(self, property_name): """Returns the desired CSS property of the element""" return await asynchronous.get_css_value( - self._remote, self._session, self._element, property_name, session_http=self._session_http + self._remote, + self._session, + self._element, + property_name, + session_http=self._session_http, ) async def screenshot(self, file): @@ -49,46 +53,71 @@ async def screenshot(self, file): path = "./" file_name = os.path.basename(file) return await asynchronous.take_screenshot_element( - self._remote, self._session, self._element, path, file_name, session_http=self._session_http + self._remote, + self._session, + self._element, + path, + file_name, + session_http=self._session_http, ) async def is_selected(self) -> bool: """Returns True if the element is selected. Otherwise returns False""" - return await asynchronous.is_element_selected(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.is_element_selected( + self._remote, self._session, self._element, session_http=self._session_http + ) async def is_enabled(self): """Returns True if the element is enabled. Otherwise returns False""" - return await asynchronous.is_element_enabled(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.is_element_enabled( + self._remote, self._session, self._element, session_http=self._session_http + ) async def get_text(self): """Returns the text of the element""" - return await asynchronous.get_text(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.get_text( + self._remote, self._session, self._element, session_http=self._session_http + ) async def get_css_value(self, property_name): """Returns the desired CSS property of the element""" return await asynchronous.get_css_value( - self._remote, self._session, self._element, property_name, session_http=self._session_http + self._remote, + self._session, + self._element, + property_name, + session_http=self._session_http, ) async def submit(self): """Submits a form""" - return await asynchronous.submit(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.submit( + self._remote, self._session, self._element, session_http=self._session_http + ) async def get_rect(self): """Returns the rectangle that enclosed the element""" - return await asynchronous.get_rect(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.get_rect( + self._remote, self._session, self._element, session_http=self._session_http + ) async def get_tag_name(self): """Returns the element tag name""" - return await asynchronous.get_tag_name(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.get_tag_name( + self._remote, self._session, self._element, session_http=self._session_http + ) async def get_computed_label(self): """Get the element tag computed label. Get the accessibility name""" - return await asynchronous.get_computed_label(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.get_computed_label( + self._remote, self._session, self._element, session_http=self._session_http + ) async def get_computed_role(self): """Get the element tag computed role (the element role)""" - return await asynchronous.get_computed_role(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.get_computed_role( + self._remote, self._session, self._element, session_http=self._session_http + ) async def get_property(self, property): """Get the given HTML property of an element, for example, 'href'""" @@ -104,15 +133,21 @@ async def get_attribute(self, attribute): async def clear(self): """Clear the element text""" - return await asynchronous.clear_element(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.clear_element( + self._remote, self._session, self._element, session_http=self._session_http + ) async def send_keys(self, text): """Fill the element with a text""" - return await asynchronous.send_keys(self._remote, self._session, self._element, text, session_http=self._session_http) + return await asynchronous.send_keys( + self._remote, self._session, self._element, text, session_http=self._session_http + ) async def click(self): """Click on the element""" - return await asynchronous.click(self._remote, self._session, self._element, session_http=self._session_http) + return await asynchronous.click( + self._remote, self._session, self._element, session_http=self._session_http + ) async def find_elements(self, locator, value): """ @@ -123,7 +158,12 @@ async def find_elements(self, locator, value): """ result = [] elements = await asynchronous.find_children_elements( - self._remote, self._session, self._element, locator, value, session_http=self._session_http + self._remote, + self._session, + self._element, + locator, + value, + session_http=self._session_http, ) for element in elements: result.append(Element(element, self._driver)) @@ -132,6 +172,11 @@ async def find_elements(self, locator, value): async def find_element(self, locator, value): """Find the element by `locator_type`""" element = await asynchronous.find_child_element( - self._remote, self._session, self._element, locator, value, session_http=self._session_http + self._remote, + self._session, + self._element, + locator, + value, + session_http=self._session_http, ) return Element(element, self._driver) diff --git a/caqui/easy/page.py b/caqui/easy/page.py index 6a877cb..8bbccbf 100644 --- a/caqui/easy/page.py +++ b/caqui/easy/page.py @@ -13,7 +13,11 @@ class AsyncPage: def __init__( - self, server_url: str, capabilities: Optional[dict] = None, url: Union[str, None] = None, session_http: ClientSession=None + self, + server_url: str, + capabilities: Optional[dict] = None, + url: Union[str, None] = None, + session_http: ClientSession = None, ) -> None: """Mimics Selenium methods""" self.session_http = session_http @@ -86,32 +90,56 @@ def quit(self): async def close(self): """Closes the window""" - return await asynchronous.close_window(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.close_window( + self._server_url, self._session, session_http=self.session_http + ) async def execute_script(self, script, args=[]): - return await asynchronous.execute_script(self._server_url, self._session, script, args, session_http=self.session_http) + return await asynchronous.execute_script( + self._server_url, self._session, script, args, session_http=self.session_http + ) async def set_window_position(self, x, y): """Repositions the page""" - rect = await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) + rect = await asynchronous.get_window_rectangle( + self._server_url, self._session, session_http=self.session_http + ) return await asynchronous.set_window_rectangle( - self._server_url, self._session, rect.get("width"), rect.get("height"), x, y, session_http=self.session_http + self._server_url, + self._session, + rect.get("width"), + rect.get("height"), + x, + y, + session_http=self.session_http, ) async def set_window_size(self, width, height): """Resizes the page""" - rect = await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) + rect = await asynchronous.get_window_rectangle( + self._server_url, self._session, session_http=self.session_http + ) return await asynchronous.set_window_rectangle( - self._server_url, self._session, width, height, rect.get("x"), rect.get("y"), session_http=self.session_http + self._server_url, + self._session, + width, + height, + rect.get("x"), + rect.get("y"), + session_http=self.session_http, ) async def get_window_position(self): """Returns the window rectangle""" - return await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.get_window_rectangle( + self._server_url, self._session, session_http=self.session_http + ) async def get_window_size(self): """Returns the window rectangle""" - return await asynchronous.get_window_rectangle(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.get_window_rectangle( + self._server_url, self._session, session_http=self.session_http + ) async def save_screenshot(self, file): """Takes a scheenshot of the page""" @@ -125,60 +153,82 @@ async def save_screenshot(self, file): async def delete_all_cookies(self): """Deletes all storaged cookies""" - return await asynchronous.delete_all_cookies(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.delete_all_cookies( + self._server_url, self._session, session_http=self.session_http + ) async def delete_cookie(self, cookie_name): """Delete the desired cookie""" - return await asynchronous.delete_cookie(self._server_url, self._session, cookie_name, session_http=self.session_http) + return await asynchronous.delete_cookie( + self._server_url, self._session, cookie_name, session_http=self.session_http + ) async def get_cookies(self): """Get all cookies""" - return await asynchronous.get_cookies(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.get_cookies( + self._server_url, self._session, session_http=self.session_http + ) async def get_cookie(self, cookie_name): """Get the desired cookie""" - return await asynchronous.get_named_cookie(self._server_url, self._session, cookie_name, session_http=self.session_http) + return await asynchronous.get_named_cookie( + self._server_url, self._session, cookie_name, session_http=self.session_http + ) async def add_cookie(self, cookie): """Add a new cookie""" - return await asynchronous.add_cookie(self._server_url, self._session, cookie, session_http=self.session_http) + return await asynchronous.add_cookie( + self._server_url, self._session, cookie, session_http=self.session_http + ) async def implicitly_wait(self, timeouts: int): """Set implicty timeouts""" - return await asynchronous.set_timeouts(self._server_url, self._session, timeouts, session_http=self.session_http) + return await asynchronous.set_timeouts( + self._server_url, self._session, timeouts, session_http=self.session_http + ) async def back(self): """This command causes the browser to traverse one step backward in the joint session history of the current browse. This is equivalent to pressing the back button in the browser.""" - return await asynchronous.go_back(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.go_back( + self._server_url, self._session, session_http=self.session_http + ) async def forward(self): """Go page forward""" - return await asynchronous.go_forward(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.go_forward( + self._server_url, self._session, session_http=self.session_http + ) async def refresh(self): """Refreshs the page""" - return await asynchronous.refresh_page(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.refresh_page( + self._server_url, self._session, session_http=self.session_http + ) async def fullscreen_window(self): """Sets the page in fullscreen""" - return await asynchronous.fullscreen_window(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.fullscreen_window( + self._server_url, self._session, session_http=self.session_http + ) async def minimize_window(self): """Minimizes the page""" - return await asynchronous.minimize_window(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.minimize_window( + self._server_url, self._session, session_http=self.session_http + ) async def maximize_window(self): """Maximizes the page""" - return await asynchronous.maximize_window(self._server_url, self._session, session_http=self.session_http) + return await asynchronous.maximize_window( + self._server_url, self._session, session_http=self.session_http + ) async def get(self, url): """Navigates to URL `url`""" await asynchronous.go_to_page( - self._server_url, - self._session, - url,session_http=self.session_http + self._server_url, self._session, url, session_http=self.session_http ) async def find_elements(self, locator, value): @@ -193,5 +243,7 @@ async def find_elements(self, locator, value): async def find_element(self, locator, value): """Find an element by a 'locator', for example 'xpath'""" - element = await asynchronous.find_element(self._server_url, self._session, locator, value, session_http=self.session_http) + element = await asynchronous.find_element( + self._server_url, self._session, locator, value, session_http=self.session_http + ) return Element(element, self) diff --git a/caqui/easy/server.py b/caqui/easy/server.py index b00bc64..89c1d00 100644 --- a/caqui/easy/server.py +++ b/caqui/easy/server.py @@ -8,6 +8,7 @@ from webdriver_manager.chrome import ChromeDriverManager from caqui.exceptions import ServerError + TIMEOUT = 120 # seconds @@ -25,18 +26,18 @@ class Server: _instance = None def __init__(self, browser: Union[DriverManager, None] = None, port=9999): - self.__browser = browser - self.__port = port - self.__process = None + self._browser = browser + self._port = port + self._sprocess = None - def __browser_factory(self): - if not self.__browser: + def _browser_factory(self): + if not self._browser: driver_manager = ChromeDriverManager().install() else: - driver_manager = self.__browser.install() + driver_manager = self._browser.install() return driver_manager - def __wait_server(self): + def _wait_server(self): MAX_RETIES = 10 for i in range(MAX_RETIES): try: @@ -45,8 +46,8 @@ def __wait_server(self): except ConnectionError: sleep(0.5) if i == (MAX_RETIES - 1): - self.__process.kill() - self.__process.wait() + self._process.kill() + self._process.wait() raise Exception("Driver not started") @staticmethod @@ -65,29 +66,29 @@ def start(self): except Exception: raise - driver_manager = self.__browser_factory() - self.__process = subprocess.Popen( - [driver_manager, f"--port={self.__port}"], + driver_manager = self._browser_factory() + self._process = subprocess.Popen( + [driver_manager, f"--port={self._port}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True, ) - if self.__process is None: + if self._process is None: raise ServerError("Not able to start the server.") - self.__wait_server() + self._wait_server() @property def url(self): """ Returns the driver URL. """ - return f"http://localhost:{self.__port}" + return f"http://localhost:{self._port}" @property def process(self): """Returns the process (PID)""" - return self.__process + return self._process def dispose(self, delay: float = 0): """ @@ -99,7 +100,7 @@ def dispose(self, delay: float = 0): """ if delay: sleep(delay) - if self.__process: - self.__process.kill() - self.__process.wait() - self.__process = None + if self._process: + self._process.kill() + self._process.wait() + self._process = None diff --git a/caqui/easy/switch_to.py b/caqui/easy/switch_to.py index afb539a..4c068d3 100644 --- a/caqui/easy/switch_to.py +++ b/caqui/easy/switch_to.py @@ -27,14 +27,20 @@ async def new_window(self, window_type): self._driver.remote, self._driver.session, window_type, session_http=self._session_http ) self._window_handle = await asynchronous.switch_to_window( - self._driver.remote, self._driver.session, self._window_handle, session_http=self._session_http + self._driver.remote, + self._driver.session, + self._window_handle, + session_http=self._session_http, ) return self._window_handle async def window(self, window_handle): """Switchs to window `window_handle`""" self._window_handle = await asynchronous.switch_to_window( - self._driver.remote, self._driver.session, window_handle, session_http=self._session_http + self._driver.remote, + self._driver.session, + window_handle, + session_http=self._session_http, ) return self._window_handle diff --git a/caqui/synchronous.py b/caqui/synchronous.py index c5ee515..38bb3ed 100644 --- a/caqui/synchronous.py +++ b/caqui/synchronous.py @@ -1,12 +1,12 @@ -import requests as __requests -import json as __json -from caqui.exceptions import WebDriverError as WebDriverError -from caqui import helper as __helper -from caqui.constants import HEADERS as __HEADERS +from requests import request +from orjson import dumps +from caqui.exceptions import WebDriverError +from caqui import helper +from caqui.constants import HEADERS from typing import Optional -def __handle_response(response): +def _handle_response(response): result = None if response.status_code in range(200, 399): result = response.json() @@ -20,43 +20,43 @@ def __handle_response(response): return result -def __get(url): +def _get(url): try: - response = __requests.request("GET", url, headers=__HEADERS, data={}) - return __handle_response(response) - except Exception as error: - raise WebDriverError("'GET' request failed.") from error + response = request("GET", url, headers=HEADERS, data={}) + return _handle_response(response) + except Exception as e: + raise WebDriverError("'GET' request failed.") from e -def __post(url, payload): +def _post(url, payload): try: - response = __requests.request( - "POST", url, headers=__HEADERS, data=__json.dumps(payload), timeout=60 + response = request( + "POST", url, headers=HEADERS, data= dumps(payload), timeout=60 ) - return __handle_response(response) - except Exception as error: - raise WebDriverError("'POST' request failed.") from error + return _handle_response(response) + except Exception as e: + raise WebDriverError("'POST' request failed.") from e -def __delete(url): +def _delete(url): try: - response = __requests.request("DELETE", url, headers={}, data={}) - return __handle_response(response) - except Exception as error: - raise WebDriverError("'DELETE' request failed.") from error + response = request("DELETE", url, headers={}, data={}) + return _handle_response(response) + except Exception as e: + raise WebDriverError("'DELETE' request failed.") from e -def __handle_alerts(server_url, session, command): +def _handle_alerts(server_url, session, command): url = f"{server_url}/session/{session}/alert/{command}" payload = {"value": command} - __post(url, payload) + _post(url, payload) return True -def __handle_window(server_url, session, command): +def _handle_window(server_url, session, command): url = f"{server_url}/session/{session}/window/{command}" payload = {} - __post(url, payload) + _post(url, payload) return True @@ -65,20 +65,20 @@ def add_cookie(server_url, session, cookie): try: url = f"{server_url}/session/{session}/cookie" payload = {"cookie": cookie} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to add cookie.") from error + except Exception as e: + raise WebDriverError("Failed to add cookie.") from e def delete_cookie(server_url, session, name): """Delete cookie by name""" try: url = f"{server_url}/session/{session}/cookie/{name}" - __delete(url) + _delete(url) return True - except Exception as error: - raise WebDriverError("Failed to delete cookie '{name}'.") from error + except Exception as e: + raise WebDriverError("Failed to delete cookie '{name}'.") from e def refresh_page(server_url, session): @@ -86,10 +86,10 @@ def refresh_page(server_url, session): try: url = f"{server_url}/session/{session}/refresh" payload = {} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to refresh page.") from error + except Exception as e: + raise WebDriverError("Failed to refresh page.") from e def go_forward(server_url, session): @@ -97,10 +97,10 @@ def go_forward(server_url, session): try: url = f"{server_url}/session/{session}/forward" payload = {} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to go page forward.") from error + except Exception as e: + raise WebDriverError("Failed to go page forward.") from e def set_window_rectangle(server_url, session, width, height, x, y): @@ -108,34 +108,34 @@ def set_window_rectangle(server_url, session, width, height, x, y): try: url = f"{server_url}/session/{session}/window/rect" payload = {"width": width, "height": height, "x": x, "y": y} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to set window rectangle.") from error + except Exception as e: + raise WebDriverError("Failed to set window rectangle.") from e def fullscreen_window(server_url, session): """Fullscreen window""" try: - return __handle_window(server_url, session, command="fullscreen") - except Exception as error: - raise WebDriverError("Failed to fullscreen window.") from error + return _handle_window(server_url, session, command="fullscreen") + except Exception as e: + raise WebDriverError("Failed to fullscreen window.") from e def minimize_window(server_url, session): """Minimize window""" try: - return __handle_window(server_url, session, command="minimize") - except Exception as error: - raise WebDriverError("Failed to minimize window.") from error + return _handle_window(server_url, session, command="minimize") + except Exception as e: + raise WebDriverError("Failed to minimize window.") from e def maximize_window(server_url, session): """Maximize window""" try: - return __handle_window(server_url, session, command="maximize") - except Exception as error: - raise WebDriverError("Failed to maximize window.") from error + return _handle_window(server_url, session, command="maximize") + except Exception as e: + raise WebDriverError("Failed to maximize window.") from e def switch_to_window(server_url, session, handle): @@ -143,10 +143,10 @@ def switch_to_window(server_url, session, handle): try: url = f"{server_url}/session/{session}/window" payload = {"name": handle} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to switch to window.") from error + except Exception as e: + raise WebDriverError("Failed to switch to window.") from e def new_window(server_url, session, window_type="tab"): @@ -158,9 +158,9 @@ def new_window(server_url, session, window_type="tab"): try: url = f"{server_url}/session/{session}/window/new" payload = {"type": window_type} - return __post(url, payload).get("value", {}).get("handle") - except Exception as error: - raise WebDriverError("Failed to open a new window.") from error + return _post(url, payload).get("value", {}).get("handle") + except Exception as e: + raise WebDriverError("Failed to open a new window.") from e def switch_to_parent_frame(server_url, session, element_frame): @@ -168,10 +168,10 @@ def switch_to_parent_frame(server_url, session, element_frame): try: url = f"{server_url}/session/{session}/frame/parent" payload = {"id": {"ELEMENT": element_frame}} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to switch to parent frame.") from error + except Exception as e: + raise WebDriverError("Failed to switch to parent frame.") from e def switch_to_frame(server_url, session, element_frame): @@ -179,20 +179,20 @@ def switch_to_frame(server_url, session, element_frame): try: url = f"{server_url}/session/{session}/frame" payload = {"id": {"ELEMENT": element_frame}} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to switch to frame.") from error + except Exception as e: + raise WebDriverError("Failed to switch to frame.") from e def delete_all_cookies(server_url, session): """Delete all cookies""" try: url = f"{server_url}/session/{session}/cookie" - __delete(url) + _delete(url) return True - except Exception as error: - raise WebDriverError("Failed to delete cookies.") from error + except Exception as e: + raise WebDriverError("Failed to delete cookies.") from e def send_alert_text(server_url, session, text): @@ -200,84 +200,84 @@ def send_alert_text(server_url, session, text): try: url = f"{server_url}/session/{session}/alert/text" payload = {"text": text} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to sent text to alert.") from error + except Exception as e: + raise WebDriverError("Failed to sent text to alert.") from e def accept_alert(server_url, session): """Accept an alert""" try: - return __handle_alerts(server_url, session, "accept") - except Exception as error: - raise WebDriverError("Failed to accept the alert.") from error + return _handle_alerts(server_url, session, "accept") + except Exception as e: + raise WebDriverError("Failed to accept the alert.") from e def dismiss_alert(server_url, session): """Dismiss an alert""" try: - return __handle_alerts(server_url, session, "dismiss") - except Exception as error: - raise WebDriverError("Failed to dismiss the alert.") from error + return _handle_alerts(server_url, session, "dismiss") + except Exception as e: + raise WebDriverError("Failed to dismiss the alert.") from e def take_screenshot_element(server_url, session, element, path="/tmp", file_name="caqui"): """Take screenshot of element.""" try: url = f"{server_url}/session/{session}/element/{element}/screenshot" - response = __get(url).get("value") - __helper.save_picture(session, path, file_name, response) + response = _get(url).get("value") + helper.save_picture(session, path, file_name, response) return True - except Exception as error: - raise WebDriverError("Failed to take screeshot.") from error + except Exception as e: + raise WebDriverError("Failed to take screeshot.") from e def take_screenshot(server_url, session, path="/tmp", file_name="caqui"): """Take screenshot.""" try: url = f"{server_url}/session/{session}/screenshot" - response = __get(url).get("value") - __helper.save_picture(session, path, file_name, response) + response = _get(url).get("value") + helper.save_picture(session, path, file_name, response) return True - except Exception as error: - raise WebDriverError("Failed to take screeshot.") from error + except Exception as e: + raise WebDriverError("Failed to take screeshot.") from e def get_named_cookie(server_url, session, name) -> str: """Get cookie by name.""" try: url = f"{server_url}/session/{session}/cookie/{name}" - return __get(url).get("value") - except Exception as error: - raise WebDriverError(f"Failed to get the cookie '{name}'.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError(f"Failed to get the cookie '{name}'.") from e def get_computed_label(server_url, session, element) -> str: """Get the element computed label. Get the accessibility name.""" try: url = f"{server_url}/session/{session}/element/{element}/computedlabel" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get the element computed label.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get the element computed label.") from e def get_computed_role(server_url, session, element) -> str: """Get the element computed role (the element role)""" try: url = f"{server_url}/session/{session}/element/{element}/computedrole" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get the element computed role.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get the element computed role.") from e def get_tag_name(server_url, session, element) -> str: """Get the element tag name""" try: url = f"{server_url}/session/{session}/element/{element}/name" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get the element name.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get the element name.") from e def get_shadow_root(server_url, session, element) -> dict: @@ -285,18 +285,18 @@ def get_shadow_root(server_url, session, element) -> dict: try: root_element = "shadow-6066-11e4-a52e-4f735466cecf" url = f"{server_url}/session/{session}/element/{element}/shadow" - return __get(url).get("value", {}).get(root_element) - except Exception as error: - raise WebDriverError("Failed to get the element shadow.") from error + return _get(url).get("value", {}).get(root_element) + except Exception as e: + raise WebDriverError("Failed to get the element shadow.") from e def get_rect(server_url, session, element) -> dict: """Get the element rectangle""" try: url = f"{server_url}/session/{session}/element/{element}/rect" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get the element rect.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get the element rect.") from e def actions_move_to_element(server_url, session, element): @@ -326,8 +326,8 @@ def actions_move_to_element(server_url, session, element): ] } return actions(server_url, session, payload) - except Exception as error: - raise WebDriverError("Failed to move to element.") from error + except Exception as e: + raise WebDriverError("Failed to move to element.") from e def actions_scroll_to_element(server_url, session, element): @@ -353,13 +353,13 @@ def actions_scroll_to_element(server_url, session, element): ] } return actions(server_url, session, payload) - except Exception as error: - raise WebDriverError("Failed to scroll to element.") from error + except Exception as e: + raise WebDriverError("Failed to scroll to element.") from e def actions(server_url, session, payload): url = f"{server_url}/session/{session}/actions" - __post(url, payload) + _post(url, payload) return True @@ -376,8 +376,8 @@ def submit(server_url, session, element): locator_value="//*[@type='submit']", ) return click(server_url, session, submit_element) - except Exception as error: - raise WebDriverError("Failed to submit form.") from error + except Exception as e: + raise WebDriverError("Failed to submit form.") from e def actions_click(server_url, session, element): @@ -413,8 +413,8 @@ def actions_click(server_url, session, element): ] } return actions(server_url, session, payload) - except Exception as error: - raise WebDriverError("Failed to click the element.") from error + except Exception as e: + raise WebDriverError("Failed to click the element.") from e def set_timeouts(server_url, session, timeouts): @@ -424,10 +424,10 @@ def set_timeouts(server_url, session, timeouts): payload = { "implicit": timeouts, } - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to set timeouts.") from error + except Exception as e: + raise WebDriverError("Failed to set timeouts.") from e def find_children_elements(server_url, session, parent_element, locator_type, locator_value): @@ -439,12 +439,12 @@ def find_children_elements(server_url, session, parent_element, locator_type, lo try: url = f"{server_url}/session/{session}/element/{parent_element}/elements" payload = {"using": locator_type, "value": locator_value, "id": parent_element} - response = __post(url, payload) - return __helper.get_elements(response) - except Exception as error: + response = _post(url, payload) + return helper.get_elements(response) + except Exception as e: raise WebDriverError( f"Failed to find the children elements from '{parent_element}'." - ) from error + ) from e def find_child_element(server_url, session, parent_element, locator_type, locator_value): @@ -452,21 +452,21 @@ def find_child_element(server_url, session, parent_element, locator_type, locato try: url = f"{server_url}/session/{session}/element/{parent_element}/element" payload = {"using": locator_type, "value": locator_value, "id": parent_element} - response = __post(url, payload) - return __helper.get_element(response) - except Exception as error: + response = _post(url, payload) + return helper.get_element(response) + except Exception as e: raise WebDriverError( f"Failed to find the child element from '{parent_element}'." - ) from error + ) from e def get_page_source(server_url, session) -> str: """Get the page source (all content)""" try: url = f"{server_url}/session/{session}/source" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get the page source.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get the page source.") from e def execute_script(server_url, session, script, args=[]): @@ -474,29 +474,29 @@ def execute_script(server_url, session, script, args=[]): try: url = f"{server_url}/session/{session}/execute/sync" payload = {"script": script, "args": args} - response = __post(url, payload) + response = _post(url, payload) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to run the script.") from error + except Exception as e: + raise WebDriverError("Failed to run the script.") from e def get_alert_text(server_url, session) -> str: """Get the text from an alert""" try: url = f"{server_url}/session/{session}/alert/text" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get the alert text.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get the alert text.") from e def get_active_element(server_url, session): """Get the active element""" try: url = f"{server_url}/session/{session}/element/active" - response = __get(url) - return __helper.get_element(response) - except Exception as error: - raise WebDriverError("Failed to get the active element.") from error + response = _get(url) + return helper.get_element(response) + except Exception as e: + raise WebDriverError("Failed to get the active element.") from e def clear_element(server_url, session, element): @@ -504,73 +504,73 @@ def clear_element(server_url, session, element): try: url = f"{server_url}/session/{session}/element/{element}/clear" payload = {"id": element} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to clear the element text.") from error + except Exception as e: + raise WebDriverError("Failed to clear the element text.") from e def is_element_enabled(server_url, session, element) -> bool: """Check if element is enabled""" try: url = f"{server_url}/session/{session}/element/{element}/enabled" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to check if element is enabled.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to check if element is enabled.") from e def get_css_value(server_url, session, element, property_name) -> str: """Get the css property value""" try: url = f"{server_url}/session/{session}/element/{element}/css/{property_name}" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get the css property value.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get the css property value.") from e def is_element_selected(server_url, session, element) -> bool: """Check if element is selected""" try: url = f"{server_url}/session/{session}/element/{element}/selected" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to check if element is selected.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to check if element is selected.") from e def get_window_rectangle(server_url, session) -> dict: """Get window rectangle""" try: url = f"{server_url}/session/{session}/window/rect" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get window rectangle.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get window rectangle.") from e def get_window_handles(server_url, session): """Get window handles""" try: url = f"{server_url}/session/{session}/window/handles" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get window handles.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get window handles.") from e def close_window(server_url, session) -> list: """Close active window""" try: url = f"{server_url}/session/{session}/window" - return __delete(url).get("value") - except Exception as error: - raise WebDriverError("Failed to close active window.") from error + return _delete(url).get("value") + except Exception as e: + raise WebDriverError("Failed to close active window.") from e def get_window(server_url, session) -> str: """Get window""" try: url = f"{server_url}/session/{session}/window" - return __get(url).get("value") - except Exception as error: - raise WebDriverError("Failed to get window.") from error + return _get(url).get("value") + except Exception as e: + raise WebDriverError("Failed to get window.") from e def go_back(server_url, session): @@ -581,20 +581,20 @@ def go_back(server_url, session): """ try: url = f"{server_url}/session/{session}/back" - __post(url, {}) + _post(url, {}) return True - except Exception as error: - raise WebDriverError("Failed to go back to page.") from error + except Exception as e: + raise WebDriverError("Failed to go back to page.") from e def get_url(server_url, session) -> str: """Return the URL from web page:""" try: url = f"{server_url}/session/{session}/url" - response = __get(url) + response = _get(url) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get page url.") from error + except Exception as e: + raise WebDriverError("Failed to get page url.") from e def get_timeouts(server_url, session) -> dict: @@ -604,29 +604,29 @@ def get_timeouts(server_url, session) -> dict: """ try: url = f"{server_url}/session/{session}/timeouts" - response = __get(url) + response = _get(url) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get timeouts.") from error + except Exception as e: + raise WebDriverError("Failed to get timeouts.") from e def get_status(server_url) -> dict: """Return the status and details of the WebDriver:""" try: url = f"{server_url}/status" - return __get(url) - except Exception as error: - raise WebDriverError("Failed to get status.") from error + return _get(url) + except Exception as e: + raise WebDriverError("Failed to get status.") from e def get_title(server_url, session) -> str: """Get the page title""" try: url = f"{server_url}/session/{session}/title" - response = __get(url) + response = _get(url) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get page title.") from error + except Exception as e: + raise WebDriverError("Failed to get page title.") from e def find_elements(server_url, session, locator_type, locator_value) -> list: @@ -634,42 +634,42 @@ def find_elements(server_url, session, locator_type, locator_value) -> list: try: url = f"{server_url}/session/{session}/elements" payload = {"using": locator_type, "value": locator_value} - response = __post(url, payload) + response = _post(url, payload) return [x.get("ELEMENT") for x in response.get("value")] - except Exception as error: + except Exception as e: raise WebDriverError( f"Failed to find elements by '{locator_type}'-'{locator_value}'." - ) from error + ) from e def get_property(server_url, session, element, property_name) -> str: """Get the given HTML property of an element, for example, 'href'""" try: url = f"{server_url}/session/{session}/element/{element}/property/{property_name}" - response = __get(url) + response = _get(url) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get value from element.") from error + except Exception as e: + raise WebDriverError("Failed to get value from element.") from e def get_attribute(server_url, session, element, attribute) -> str: """Get the given HTML attribute of an element, for example, 'aria-valuenow'""" try: url = f"{server_url}/session/{session}/element/{element}/attribute/{attribute}" - response = __get(url) + response = _get(url) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get value from element.") from error + except Exception as e: + raise WebDriverError("Failed to get value from element.") from e def get_cookies(server_url, session) -> list: """Get the page cookies""" try: url = f"{server_url}/session/{session}/cookie" - response = __get(url) + response = _get(url) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get page cookies.") from error + except Exception as e: + raise WebDriverError("Failed to get page cookies.") from e def get(server_url, session, page_url): @@ -682,30 +682,30 @@ def go_to_page(server_url, session, page_url): try: url = f"{server_url}/session/{session}/url" payload = {"url": page_url} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError(f"Failed to navigate to '{page_url}'") from error + except Exception as e: + raise WebDriverError(f"Failed to navigate to '{page_url}'") from e def close_session(server_url, session): """Close an opened session and close the browser""" try: url = f"{server_url}/session/{session}" - __delete(url) + _delete(url) return True - except Exception as error: - raise WebDriverError("Failed to close session.") from error + except Exception as e: + raise WebDriverError("Failed to close session.") from e def get_text(server_url, session, element) -> str: """Get the text of an element""" try: url = f"{server_url}/session/{session}/element/{element}/text" - response = __get(url) + response = _get(url) return response.get("value") - except Exception as error: - raise WebDriverError("Failed to get text from element.") from error + except Exception as e: + raise WebDriverError("Failed to get text from element.") from e def send_keys(server_url, session, element, text): @@ -713,10 +713,10 @@ def send_keys(server_url, session, element, text): try: url = f"{server_url}/session/{session}/element/{element}/value" payload = {"text": text, "value": [*text], "id": element} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError(f"Failed to send key '{text}'.") from error + except Exception as e: + raise WebDriverError(f"Failed to send key '{text}'.") from e def click(server_url, session, element): @@ -724,13 +724,13 @@ def click(server_url, session, element): try: url = f"{server_url}/session/{session}/element/{element}/click" payload = {"id": element} - __post(url, payload) + _post(url, payload) return True - except Exception as error: - raise WebDriverError("Failed to click on element.") from error + except Exception as e: + raise WebDriverError("Failed to click on element.") from e -def __get_session(response) -> str: +def _get_session(response) -> str: # Firefox response value = response.get("value") session_id = value.get("sessionId") @@ -750,10 +750,10 @@ def get_session(server_url: str, capabilities: Optional[dict] = None): url = f"{server_url}/session" if not capabilities: capabilities = {} - response = __post(url, payload=capabilities) - return __get_session(response) - except Exception as error: - raise WebDriverError("Failed to open session. Check the browser capabilities.") from error + response = _post(url, payload=capabilities) + return _get_session(response) + except Exception as e: + raise WebDriverError("Failed to open session. Check the browser capabilities.") from e def find_element(server_url, session, locator_type, locator_value) -> dict: @@ -761,15 +761,15 @@ def find_element(server_url, session, locator_type, locator_value) -> dict: try: url = f"{server_url}/session/{session}/element" payload = {"using": locator_type, "value": locator_value} - response = __post(url, payload) + response = _post(url, payload) # Firefox does not support id locator, so it prints the error message to the user # It helps on debug if response.get("value").get("error"): raise WebDriverError(f"Failed to find element. {response}") - return __helper.get_element(response) - except Exception as error: + return helper.get_element(response) + except Exception as e: raise WebDriverError( f"Failed to find element by '{locator_type}'-'{locator_value}'." - ) from error + ) from e diff --git a/dev-requirements.txt b/dev-requirements.txt index 0267580..9d9ca4f 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,5 @@ requests aiohttp webdriver_manager -types-requests \ No newline at end of file +types-requests +orjson \ No newline at end of file diff --git a/samples/sample-web-driver.py b/samples/sample-web-driver.py index 1e7b9f5..13295a6 100644 --- a/samples/sample-web-driver.py +++ b/samples/sample-web-driver.py @@ -28,7 +28,7 @@ async def get_all_links(server): all_anchors = [] for i in range(4): i += 1 - anchors = await __get_links(server_url, session, i) + anchors = await _get_links(server_url, session, i) all_anchors.extend(anchors) for anchor in all_anchors: @@ -38,7 +38,7 @@ async def get_all_links(server): synchronous.close_session(server_url, session) -async def __get_links(server_url, session, i): +async def _get_links(server_url, session, i): locator_value = f"//a[@id='a{i}']" locator_type = "xpath" anchors = [] diff --git a/tests/conftest.py b/tests/conftest.py index f4830e8..4a1d562 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,7 @@ CAPTURES = "captures" -def __build_capabilities(): +def _build_capabilities(): options = ChromeOptionsBuilder().args(["headless"]) capabilities = ( ChromeCapabilitiesBuilder() @@ -28,14 +28,14 @@ def __build_capabilities(): def setup_server(): server = Server.get_instance(port=SERVER_PORT) server.start() - yield - server.dispose(delay=3) + # yield + # server.dispose(delay=3) @fixture def setup_functional_environment(): server_url = SERVER_URL - capabilities = __build_capabilities() + capabilities = _build_capabilities() session = synchronous.get_session(server_url, capabilities) synchronous.go_to_page( server_url, @@ -50,10 +50,11 @@ def setup_functional_environment(): finally: synchronous.close_session(server_url, session) + @pytest_asyncio.fixture async def setup_environment(): server_url = SERVER_URL - capabilities = __build_capabilities() + capabilities = _build_capabilities() async with ClientSession() as session_http: page = AsyncPage(server_url, capabilities, PAGE_URL, session_http=session_http) yield page diff --git a/tests/feature/test_async_with_http_session.py b/tests/feature/test_async_with_http_session.py index d35c5fc..60e150e 100644 --- a/tests/feature/test_async_with_http_session.py +++ b/tests/feature/test_async_with_http_session.py @@ -4,84 +4,10 @@ from caqui.exceptions import WebDriverError from caqui.by import By from tests.constants import COOKIE -from caqui.easy import AsyncPage -from tests.constants import PAGE_URL from pytest import mark from tests.constants import COOKIE -@mark.asyncio -async def test_big_scenario_of_functions_with_session_http(setup_environment: AsyncPage): - page = setup_environment - await page.implicitly_wait(10) - - # Need to navigate to a web page. If use 'playgound.html' the error - # 'Document is cookie-averse' happens - await page.get( - "https://example.org/", - ) - cookies = COOKIE - await page.add_cookie(cookies) - cookie = (await page.get_cookies())[0] - cookie["name"] = "other" - await page.add_cookie(cookie) - await page.delete_cookie("other") - await page.delete_all_cookies() - await page.get( - PAGE_URL, - ) - - await page.switch_to.active_element.get_attribute("value") - element = await page.find_element(By.XPATH, "//a") - # Returns and base64 encoded string into image - await element.screenshot("/tmp/image.png") - - await page.back() - await page.forward() - await page.refresh() - - alert_element = await page.find_element(By.CSS_SELECTOR, "#alert-button-prompt") - await alert_element.click() - alert_object = page.switch_to.alert - await page.alert.accept() - - await alert_element.click() - await alert_object.send_keys("Caqui") - await alert_object.dismiss() - - iframe = await page.find_element(By.ID, "my-iframe") - # switch to selected iframe - await page.switch_to.frame(iframe) - await page.switch_to.default_content() - # switching to second iframe based on index - iframe = (await page.find_elements(By.ID, "my-iframe"))[0] - - # switch to selected iframe - await page.switch_to.frame(iframe) - # switch back to default content - await page.switch_to.default_content() - - window_handle = page.current_window_handle - assert len(page.window_handles) >= 1 - await page.switch_to.window(window_handle) - # Opens a new tab and switches to new tab - await page.switch_to.new_window("tab") - # Opens a new window and switches to new window - await page.switch_to.new_window("window") - - # Access each dimension individually - await page.set_window_size(1024, 768) - # Move the window to the top left of the primary monitor - await page.set_window_position(0, 0) - await page.maximize_window() - # await driver.minimize_window() # does not work on headless mode - await page.save_screenshot("/tmp/image.png") - - # Executing JavaScript to capture innerText of header element - await page.execute_script('alert("any warn")') - - - @mark.asyncio async def test_add_cookie(setup_functional_environment): server_url, session = setup_functional_environment @@ -103,9 +29,7 @@ async def test_add_cookie(setup_functional_environment): async with ClientSession() as session_http: assert ( - await asynchronous.add_cookie( - server_url, session, cookie, session_http=session_http - ) + await asynchronous.add_cookie(server_url, session, cookie, session_http=session_http) is True ) cookies_after = synchronous.get_cookies(server_url, session) @@ -122,9 +46,7 @@ async def test_delete_cookie_asynchronous(setup_functional_environment): async with ClientSession() as session_http: assert ( - await asynchronous.delete_cookie( - server_url, session, name, session_http=session_http - ) + await asynchronous.delete_cookie(server_url, session, name, session_http=session_http) is True ) cookies = synchronous.get_cookies(server_url, session) @@ -163,10 +85,7 @@ async def test_refresh_page(setup_functional_environment): element_before = element_after async with ClientSession() as session_http: assert ( - await asynchronous.refresh_page( - server_url, session, session_http=session_http - ) - is True + await asynchronous.refresh_page(server_url, session, session_http=session_http) is True ) element_after = synchronous.find_element(server_url, session, By.XPATH, "//input") @@ -190,12 +109,7 @@ async def test_go_forward(setup_functional_environment): synchronous.go_back(server_url, session) async with ClientSession() as session_http: - assert ( - await asynchronous.go_forward( - server_url, session, session_http=session_http - ) - is True - ) + assert await asynchronous.go_forward(server_url, session, session_http=session_http) is True assert synchronous.get_title(server_url, session) == title @@ -208,10 +122,7 @@ async def test_set_window_rectangle(setup_functional_environment): x = window_rectangle_before.get("x") + 1 y = window_rectangle_before.get("y") + 1 - assert ( - synchronous.set_window_rectangle(server_url, session, width, height, x, y) - is True - ) + assert synchronous.set_window_rectangle(server_url, session, width, height, x, y) is True window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before @@ -256,9 +167,7 @@ async def test_fullscreen_window(setup_functional_environment): async with ClientSession() as session_http: assert ( - await asynchronous.fullscreen_window( - server_url, session, session_http=session_http - ) + await asynchronous.fullscreen_window(server_url, session, session_http=session_http) is True ) @@ -286,9 +195,7 @@ async def test_minimize_window(setup_functional_environment): async with ClientSession() as session_http: assert ( - await asynchronous.minimize_window( - server_url, session, session_http=session_http - ) + await asynchronous.minimize_window(server_url, session, session_http=session_http) is True ) @@ -307,9 +214,7 @@ async def test_maximize_window_asynchronous(setup_functional_environment): async with ClientSession() as session_http: assert ( - await asynchronous.maximize_window( - server_url, session, session_http=session_http - ) + await asynchronous.maximize_window(server_url, session, session_http=session_http) is True ) @@ -381,9 +286,7 @@ async def test_switch_to_parent_frame_asynchronous(setup_functional_environment) locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) async with ClientSession() as session_http: assert ( await asynchronous.switch_to_parent_frame( @@ -398,12 +301,8 @@ def test_switch_to_parent_frame_synchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) - assert ( - synchronous.switch_to_parent_frame(server_url, session, element_frame) is True - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) + assert synchronous.switch_to_parent_frame(server_url, session, element_frame) is True @mark.asyncio @@ -412,12 +311,13 @@ async def test_switch_to_frame_asynchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) async with ClientSession() as session_http: assert ( - await asynchronous.switch_to_frame(server_url, session, element_frame, session_http=session_http) is True + await asynchronous.switch_to_frame( + server_url, session, element_frame, session_http=session_http + ) + is True ) @@ -426,9 +326,7 @@ def test_switch_to_frame_synchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.switch_to_frame(server_url, session, element_frame) is True @@ -446,7 +344,12 @@ async def test_send_alert_text(setup_functional_environment): synchronous.click(server_url, session, element) async with ClientSession() as session_http: - assert await asynchronous.send_alert_text(server_url, session, "any2", session_http=session_http) is True + assert ( + await asynchronous.send_alert_text( + server_url, session, "any2", session_http=session_http + ) + is True + ) synchronous.accept_alert(server_url, session) is True @@ -463,7 +366,9 @@ async def test_accept_alert(setup_functional_environment): synchronous.click(server_url, session, element) async with ClientSession() as session_http: - assert await asynchronous.accept_alert(server_url, session, session_http=session_http) is True + assert ( + await asynchronous.accept_alert(server_url, session, session_http=session_http) is True + ) @mark.asyncio @@ -479,7 +384,9 @@ async def test_dismiss_alert(setup_functional_environment): synchronous.click(server_url, session, element) async with ClientSession() as session_http: - assert await asynchronous.dismiss_alert(server_url, session, session_http=session_http) is True + assert ( + await asynchronous.dismiss_alert(server_url, session, session_http=session_http) is True + ) @mark.asyncio @@ -507,9 +414,7 @@ async def test_take_screenshot(setup_functional_environment): assert synchronous.take_screenshot(server_url, session) is True async with ClientSession() as session_http: assert ( - await asynchronous.take_screenshot( - server_url, session, session_http=session_http - ) + await asynchronous.take_screenshot(server_url, session, session_http=session_http) is True ) @@ -522,7 +427,9 @@ async def test_delete_cookies_asynchronous(setup_functional_environment): cookies_before = synchronous.get_cookies(server_url, session) async with ClientSession() as session_http: - response = await asynchronous.delete_all_cookies(server_url, session, session_http=session_http) + response = await asynchronous.delete_all_cookies( + server_url, session, session_http=session_http + ) assert response is True cookies_after = synchronous.get_cookies(server_url, session) @@ -549,11 +456,11 @@ async def test_get_named_cookie(setup_functional_environment): name = "username" # cookie created on page load expected = "John Doe" - assert ( - synchronous.get_named_cookie(server_url, session, name).get("value") == expected - ) + assert synchronous.get_named_cookie(server_url, session, name).get("value") == expected async with ClientSession() as session_http: - response = await asynchronous.get_named_cookie(server_url, session, name, session_http=session_http) + response = await asynchronous.get_named_cookie( + server_url, session, name, session_http=session_http + ) assert response.get("value") == expected @@ -570,7 +477,10 @@ async def test_get_computed_label(setup_functional_environment): async with ClientSession() as session_http: assert ( - await asynchronous.get_computed_label(server_url, session, element, session_http=session_http) == expected + await asynchronous.get_computed_label( + server_url, session, element, session_http=session_http + ) + == expected ) @@ -587,7 +497,10 @@ async def test_get_computed_role(setup_functional_environment): async with ClientSession() as session_http: assert ( - await asynchronous.get_computed_role(server_url, session, element, session_http=session_http) == expected + await asynchronous.get_computed_role( + server_url, session, element, session_http=session_http + ) + == expected ) @@ -602,16 +515,15 @@ async def test_get_tag_name(setup_functional_environment): assert synchronous.get_tag_name(server_url, session, element) == expected async with ClientSession() as session_http: - assert await asynchronous.get_tag_name(server_url, session, element, session_http=session_http) == expected + assert ( + await asynchronous.get_tag_name(server_url, session, element, session_http=session_http) + == expected + ) -@mark.parametrize( - "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] -) +@mark.parametrize("locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")]) @mark.asyncio -async def test_find_element_from_shadow_root( - setup_functional_environment, locator, value -): +async def test_find_element_from_shadow_root(setup_functional_environment, locator, value): server_url, session = setup_functional_environment locator_type = By.ID locator_value = "shadow-root" @@ -620,9 +532,7 @@ async def test_find_element_from_shadow_root( shadow_root = synchronous.get_shadow_root(server_url, session, element) - actual = synchronous.find_child_element( - server_url, session, shadow_root, locator, value - ) + actual = synchronous.find_child_element(server_url, session, shadow_root, locator, value) assert actual is not None async with ClientSession() as session_http: @@ -633,13 +543,9 @@ async def test_find_element_from_shadow_root( assert actual is not None -@mark.parametrize( - "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] -) +@mark.parametrize("locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")]) @mark.asyncio -async def test_find_elements_from_shadow_root( - setup_functional_environment, locator, value -): +async def test_find_elements_from_shadow_root(setup_functional_environment, locator, value): server_url, session = setup_functional_environment locator_type = By.ID locator_value = "shadow-root" @@ -649,9 +555,7 @@ async def test_find_elements_from_shadow_root( shadow_root = synchronous.get_shadow_root(server_url, session, element) - actual = synchronous.find_children_elements( - server_url, session, shadow_root, locator, value - ) + actual = synchronous.find_children_elements(server_url, session, shadow_root, locator, value) assert len(actual) == one @@ -674,7 +578,9 @@ async def test_get_shadow_root(setup_functional_environment): assert synchronous.get_shadow_root(server_url, session, element) is not None async with ClientSession() as session_http: - response = await asynchronous.get_shadow_root(server_url, session, element, session_http=session_http) + response = await asynchronous.get_shadow_root( + server_url, session, element, session_http=session_http + ) assert response is not None @@ -689,7 +595,10 @@ async def test_get_rect(setup_functional_environment): assert synchronous.get_rect(server_url, session, element) == expected async with ClientSession() as session_http: - assert await asynchronous.get_rect(server_url, session, element, session_http=session_http) == expected + assert ( + await asynchronous.get_rect(server_url, session, element, session_http=session_http) + == expected + ) @mark.asyncio @@ -702,7 +611,10 @@ async def test_move_to_element(setup_functional_environment): assert synchronous.actions_move_to_element(server_url, session, element) is True async with ClientSession() as session_http: assert ( - await asynchronous.actions_move_to_element(server_url, session, element, session_http=session_http) is True + await asynchronous.actions_move_to_element( + server_url, session, element, session_http=session_http + ) + is True ) @@ -716,7 +628,9 @@ async def test_actions_scroll_to_element(setup_functional_environment): assert synchronous.actions_scroll_to_element(server_url, session, element) is True async with ClientSession() as session_http: assert ( - await asynchronous.actions_scroll_to_element(server_url, session, element, session_http=session_http) + await asynchronous.actions_scroll_to_element( + server_url, session, element, session_http=session_http + ) is True ) @@ -733,7 +647,10 @@ async def test_submit(setup_functional_environment): synchronous.refresh_page(server_url, session) element = synchronous.find_element(server_url, session, locator_type, locator_value) async with ClientSession() as session_http: - assert await asynchronous.submit(server_url, session, element, session_http=session_http) is True + assert ( + await asynchronous.submit(server_url, session, element, session_http=session_http) + is True + ) @mark.asyncio @@ -745,7 +662,12 @@ async def test_actions_click(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.actions_click(server_url, session, element) is True async with ClientSession() as session_http: - assert await asynchronous.actions_click(server_url, session, element,session_http=session_http) is True + assert ( + await asynchronous.actions_click( + server_url, session, element, session_http=session_http + ) + is True + ) @mark.asyncio @@ -797,7 +719,12 @@ async def test_find_children_elements(setup_functional_environment): assert len(children_elements) > expected async with ClientSession() as session_http: children_elements = await asynchronous.find_children_elements( - server_url, session, parent_element, locator_type, locator_value, session_http=session_http + server_url, + session, + parent_element, + locator_type, + locator_value, + session_http=session_http, ) assert len(children_elements) > expected @@ -823,7 +750,12 @@ async def test_find_child_element(setup_functional_environment): assert text == expected async with ClientSession() as session_http: child_element = await asynchronous.find_child_element( - server_url, session, parent_element, locator_type, locator_value, session_http=session_http + server_url, + session, + parent_element, + locator_type, + locator_value, + session_http=session_http, ) text = synchronous.get_text(server_url, session, child_element) assert text == expected @@ -836,7 +768,9 @@ async def test_get_page_source(setup_functional_environment): assert expected in synchronous.get_page_source(server_url, session) async with ClientSession() as session_http: - assert expected in await asynchronous.get_page_source(server_url, session, session_http=session_http) + assert expected in await asynchronous.get_page_source( + server_url, session, session_http=session_http + ) @mark.asyncio @@ -844,7 +778,12 @@ async def test_execute_script_asynchronous(setup_functional_environment): server_url, session = setup_functional_environment script = "alert('any warn')" async with ClientSession() as session_http: - assert await asynchronous.execute_script(server_url, session, script, session_http=session_http) is None + assert ( + await asynchronous.execute_script( + server_url, session, script, session_http=session_http + ) + is None + ) def test_execute_script_synchronous(setup_functional_environment): @@ -860,14 +799,15 @@ async def test_get_alert_text(setup_functional_environment): locator_value = "#alert-button" expected = "any warn" - alert_button = synchronous.find_element( - server_url, session, locator_type, locator_value - ) + alert_button = synchronous.find_element(server_url, session, locator_type, locator_value) synchronous.click(server_url, session, alert_button) assert synchronous.get_alert_text(server_url, session) == expected async with ClientSession() as session_http: - assert await asynchronous.get_alert_text(server_url, session, session_http=session_http) == expected + assert ( + await asynchronous.get_alert_text(server_url, session, session_http=session_http) + == expected + ) @mark.asyncio @@ -881,7 +821,10 @@ async def test_get_active_element(setup_functional_environment): assert synchronous.get_active_element(server_url, session) == element async with ClientSession() as session_http: - assert await asynchronous.get_active_element(server_url, session, session_http=session_http) == element + assert ( + await asynchronous.get_active_element(server_url, session, session_http=session_http) + == element + ) @mark.asyncio @@ -894,7 +837,9 @@ async def test_clear_element_fails_when_invalid_inputs(setup_functional_environm with raises(WebDriverError): async with ClientSession() as session_http: - await asynchronous.clear_element(server_url, session, element, session_http=session_http) + await asynchronous.clear_element( + server_url, session, element, session_http=session_http + ) @mark.asyncio @@ -910,7 +855,12 @@ async def test_clear_element(setup_functional_environment): synchronous.send_keys(server_url, session, element, text) async with ClientSession() as session_http: - assert await asynchronous.clear_element(server_url, session, element, session_http=session_http) is True + assert ( + await asynchronous.clear_element( + server_url, session, element, session_http=session_http + ) + is True + ) @mark.asyncio @@ -923,7 +873,12 @@ async def test_is_element_enabled(setup_functional_environment): assert synchronous.is_element_enabled(server_url, session, element) is True async with ClientSession() as session_http: - assert await asynchronous.is_element_enabled(server_url, session, element, session_http=session_http) is True + assert ( + await asynchronous.is_element_enabled( + server_url, session, element, session_http=session_http + ) + is True + ) @mark.asyncio @@ -936,13 +891,12 @@ async def test_get_css_value(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) - assert ( - synchronous.get_css_value(server_url, session, element, property_name) - == expected - ) + assert synchronous.get_css_value(server_url, session, element, property_name) == expected async with ClientSession() as session_http: assert ( - await asynchronous.get_css_value(server_url, session, element, property_name, session_http=session_http) + await asynchronous.get_css_value( + server_url, session, element, property_name, session_http=session_http + ) == expected ) @@ -957,8 +911,12 @@ async def test_is_element_selected(setup_functional_environment): assert synchronous.is_element_selected(server_url, session, element) is False async with ClientSession() as session_http: - assert await asynchronous.is_element_selected(server_url, session, element, session_http=session_http) is False - + assert ( + await asynchronous.is_element_selected( + server_url, session, element, session_http=session_http + ) + is False + ) @mark.asyncio @@ -968,7 +926,9 @@ async def test_get_window_rectangle(setup_functional_environment): assert expected in synchronous.get_window_rectangle(server_url, session) async with ClientSession() as session_http: - rectangle = await asynchronous.get_window_rectangle(server_url, session, session_http=session_http) + rectangle = await asynchronous.get_window_rectangle( + server_url, session, session_http=session_http + ) assert expected in rectangle @@ -978,7 +938,9 @@ async def test_get_window_handles(setup_functional_environment): assert isinstance(synchronous.get_window_handles(server_url, session), list) async with ClientSession() as session_http: - handles = await asynchronous.get_window_handles(server_url, session, session_http=session_http) + handles = await asynchronous.get_window_handles( + server_url, session, session_http=session_http + ) assert isinstance(handles, list) @@ -1001,7 +963,10 @@ async def test_get_window(setup_functional_environment): assert synchronous.get_window(server_url, session) is not None async with ClientSession() as session_http: - assert await asynchronous.get_window(server_url, session, session_http=session_http) is not None + assert ( + await asynchronous.get_window(server_url, session, session_http=session_http) + is not None + ) @mark.asyncio @@ -1015,7 +980,9 @@ async def test_get_attribute_fails_when_invalid_attribute(setup_functional_envir with raises(WebDriverError): async with ClientSession() as session_http: - await asynchronous.get_attribute(server_url, session, element, attribute, session_http=session_http) + await asynchronous.get_attribute( + server_url, session, element, attribute, session_http=session_http + ) @mark.asyncio @@ -1024,13 +991,12 @@ async def test_get_attribute(setup_functional_environment): attribute = "href" element = synchronous.find_element(server_url, session, By.XPATH, "//a[@id='a1']") - assert ( - synchronous.get_attribute(server_url, session, element, attribute) - == "http://any1.com/" - ) + assert synchronous.get_attribute(server_url, session, element, attribute) == "http://any1.com/" async with ClientSession() as session_http: assert ( - await asynchronous.get_attribute(server_url, session, element, attribute, session_http=session_http) + await asynchronous.get_attribute( + server_url, session, element, attribute, session_http=session_http + ) == "http://any1.com/" ) @@ -1065,7 +1031,9 @@ async def test_get_url(setup_functional_environment): assert expected in synchronous.get_url(server_url, session) async with ClientSession() as session_http: - assert expected in await asynchronous.get_url(server_url, session, session_http=session_http) + assert expected in await asynchronous.get_url( + server_url, session, session_http=session_http + ) @mark.asyncio @@ -1075,7 +1043,9 @@ async def test_get_timeouts(setup_functional_environment): assert expected in synchronous.get_timeouts(server_url, session) async with ClientSession() as session_http: - assert expected in await asynchronous.get_timeouts(server_url, session, session_http=session_http) + assert expected in await asynchronous.get_timeouts( + server_url, session, session_http=session_http + ) @mark.asyncio @@ -1095,7 +1065,9 @@ async def test_get_title(setup_functional_environment): assert synchronous.get_title(server_url, session) == expected async with ClientSession() as session_http: - assert await asynchronous.get_title(server_url, session, session_http=session_http) == expected + assert ( + await asynchronous.get_title(server_url, session, session_http=session_http) == expected + ) @mark.asyncio @@ -1122,9 +1094,7 @@ async def test_find_elements(setup_functional_environment): locator_type = By.XPATH locator_value = "//input" - elements = synchronous.find_elements( - server_url, session, locator_type, locator_value - ) + elements = synchronous.find_elements(server_url, session, locator_type, locator_value) async with ClientSession() as session_http: async_elements = await asynchronous.find_elements( server_url, session, locator_type, locator_value, session_http=session_http @@ -1156,10 +1126,7 @@ async def test_find_element(setup_functional_environment): locator_type = By.XPATH locator_value = "//input" - assert ( - synchronous.find_element(server_url, session, locator_type, locator_value) - is not None - ) + assert synchronous.find_element(server_url, session, locator_type, locator_value) is not None async with ClientSession() as session_http: assert ( await asynchronous.find_element( @@ -1200,9 +1167,7 @@ async def test_get_text(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) async with ClientSession() as session_http: assert ( - await asynchronous.get_text( - server_url, session, element, session_http=session_http - ) + await asynchronous.get_text(server_url, session, element, session_http=session_http) == expected ) assert synchronous.get_text(server_url, session, element) == expected @@ -1237,9 +1202,7 @@ async def test_click(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) async with ClientSession() as session_http: assert ( - await asynchronous.click( - server_url, session, element, session_http=session_http - ) + await asynchronous.click(server_url, session, element, session_http=session_http) is True ) assert synchronous.click(server_url, session, element) is True diff --git a/tests/feature/test_sync_and_async.py b/tests/feature/test_sync_and_async.py index b02cee9..389c402 100644 --- a/tests/feature/test_sync_and_async.py +++ b/tests/feature/test_sync_and_async.py @@ -108,10 +108,7 @@ async def test_set_window_rectangle(setup_functional_environment): x = window_rectangle_before.get("x") + 1 y = window_rectangle_before.get("y") + 1 - assert ( - synchronous.set_window_rectangle(server_url, session, width, height, x, y) - is True - ) + assert synchronous.set_window_rectangle(server_url, session, width, height, x, y) is True window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before @@ -122,12 +119,7 @@ async def test_set_window_rectangle(setup_functional_environment): synchronous.maximize_window(server_url, session) - assert ( - await asynchronous.set_window_rectangle( - server_url, session, width, height, x, y - ) - is True - ) + assert await asynchronous.set_window_rectangle(server_url, session, width, height, x, y) is True window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) @@ -228,10 +220,7 @@ async def test_switch_to_window(setup_functional_environment, window_type): assert synchronous.get_title(server_url, session) == "" synchronous.switch_to_window(server_url, session, handle=sample_page) is True - assert ( - await asynchronous.switch_to_window(server_url, session, handle=new_page) - is True - ) + assert await asynchronous.switch_to_window(server_url, session, handle=new_page) is True assert synchronous.get_title(server_url, session) == "" @@ -253,13 +242,8 @@ async def test_switch_to_parent_frame_asynchronous(setup_functional_environment) locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) - assert ( - await asynchronous.switch_to_parent_frame(server_url, session, element_frame) - is True - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) + assert await asynchronous.switch_to_parent_frame(server_url, session, element_frame) is True def test_switch_to_parent_frame_synchronous(setup_functional_environment): @@ -267,12 +251,8 @@ def test_switch_to_parent_frame_synchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) - assert ( - synchronous.switch_to_parent_frame(server_url, session, element_frame) is True - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) + assert synchronous.switch_to_parent_frame(server_url, session, element_frame) is True @mark.asyncio @@ -281,12 +261,8 @@ async def test_switch_to_frame_asynchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) - assert ( - await asynchronous.switch_to_frame(server_url, session, element_frame) is True - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) + assert await asynchronous.switch_to_frame(server_url, session, element_frame) is True def test_switch_to_frame_synchronous(setup_functional_environment): @@ -294,9 +270,7 @@ def test_switch_to_frame_synchronous(setup_functional_environment): locator_type = By.ID locator_value = "my-iframe" - element_frame = synchronous.find_element( - server_url, session, locator_type, locator_value - ) + element_frame = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.switch_to_frame(server_url, session, element_frame) is True @@ -407,9 +381,7 @@ async def test_get_named_cookie(setup_functional_environment): name = "username" # cookie created on page load expected = "John Doe" - assert ( - synchronous.get_named_cookie(server_url, session, name).get("value") == expected - ) + assert synchronous.get_named_cookie(server_url, session, name).get("value") == expected response = await asynchronous.get_named_cookie(server_url, session, name) assert response.get("value") == expected @@ -425,9 +397,7 @@ async def test_get_computed_label(setup_functional_environment): assert synchronous.get_computed_label(server_url, session, element) == expected - assert ( - await asynchronous.get_computed_label(server_url, session, element) == expected - ) + assert await asynchronous.get_computed_label(server_url, session, element) == expected @mark.asyncio @@ -441,9 +411,7 @@ async def test_get_computed_role(setup_functional_environment): assert synchronous.get_computed_role(server_url, session, element) == expected - assert ( - await asynchronous.get_computed_role(server_url, session, element) == expected - ) + assert await asynchronous.get_computed_role(server_url, session, element) == expected @mark.asyncio @@ -460,13 +428,9 @@ async def test_get_tag_name(setup_functional_environment): assert await asynchronous.get_tag_name(server_url, session, element) == expected -@mark.parametrize( - "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] -) +@mark.parametrize("locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")]) @mark.asyncio -async def test_find_element_from_shadow_root( - setup_functional_environment, locator, value -): +async def test_find_element_from_shadow_root(setup_functional_environment, locator, value): server_url, session = setup_functional_environment locator_type = By.ID locator_value = "shadow-root" @@ -475,26 +439,18 @@ async def test_find_element_from_shadow_root( shadow_root = synchronous.get_shadow_root(server_url, session, element) - actual = synchronous.find_child_element( - server_url, session, shadow_root, locator, value - ) + actual = synchronous.find_child_element(server_url, session, shadow_root, locator, value) assert actual is not None - actual = await asynchronous.find_child_element( - server_url, session, shadow_root, locator, value - ) + actual = await asynchronous.find_child_element(server_url, session, shadow_root, locator, value) assert actual is not None -@mark.parametrize( - "locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")] -) +@mark.parametrize("locator, value", [(By.ID, "shadow-button"), (By.CSS_SELECTOR, "button")]) @mark.asyncio -async def test_find_elements_from_shadow_root( - setup_functional_environment, locator, value -): +async def test_find_elements_from_shadow_root(setup_functional_environment, locator, value): server_url, session = setup_functional_environment locator_type = By.ID locator_value = "shadow-root" @@ -504,9 +460,7 @@ async def test_find_elements_from_shadow_root( shadow_root = synchronous.get_shadow_root(server_url, session, element) - actual = synchronous.find_children_elements( - server_url, session, shadow_root, locator, value - ) + actual = synchronous.find_children_elements(server_url, session, shadow_root, locator, value) assert len(actual) == one @@ -553,9 +507,7 @@ async def test_move_to_element(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.actions_move_to_element(server_url, session, element) is True - assert ( - await asynchronous.actions_move_to_element(server_url, session, element) is True - ) + assert await asynchronous.actions_move_to_element(server_url, session, element) is True @mark.asyncio @@ -566,10 +518,7 @@ async def test_actions_scroll_to_element(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.actions_scroll_to_element(server_url, session, element) is True - assert ( - await asynchronous.actions_scroll_to_element(server_url, session, element) - is True - ) + assert await asynchronous.actions_scroll_to_element(server_url, session, element) is True @mark.asyncio @@ -607,9 +556,7 @@ async def test_raise_exception_when_element_not_found(setup_functional_environme synchronous.find_element(server_url, session, locator_type, locator_value) with raises(WebDriverError): - await asynchronous.find_element( - server_url, session, locator_type, locator_value - ) + await asynchronous.find_element(server_url, session, locator_type, locator_value) @mark.asyncio @@ -707,9 +654,7 @@ async def test_get_alert_text(setup_functional_environment): locator_value = "#alert-button" expected = "any warn" - alert_button = synchronous.find_element( - server_url, session, locator_type, locator_value - ) + alert_button = synchronous.find_element(server_url, session, locator_type, locator_value) synchronous.click(server_url, session, alert_button) assert synchronous.get_alert_text(server_url, session) == expected @@ -778,14 +723,8 @@ async def test_get_css_value(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) - assert ( - synchronous.get_css_value(server_url, session, element, property_name) - == expected - ) - assert ( - await asynchronous.get_css_value(server_url, session, element, property_name) - == expected - ) + assert synchronous.get_css_value(server_url, session, element, property_name) == expected + assert await asynchronous.get_css_value(server_url, session, element, property_name) == expected @mark.asyncio @@ -859,10 +798,7 @@ async def test_get_attribute(setup_functional_environment): attribute = "href" element = synchronous.find_element(server_url, session, By.XPATH, "//a[@id='a1']") - assert ( - synchronous.get_attribute(server_url, session, element, attribute) - == "http://any1.com/" - ) + assert synchronous.get_attribute(server_url, session, element, attribute) == "http://any1.com/" assert ( await asynchronous.get_attribute(server_url, session, element, attribute) == "http://any1.com/" @@ -938,9 +874,7 @@ async def test_find_elements_fails_when_invalid_data_input( synchronous.find_elements(server_url, session, locator_type, locator_value) with raises(WebDriverError): - await asynchronous.find_elements( - server_url, session, locator_type, locator_value - ) + await asynchronous.find_elements(server_url, session, locator_type, locator_value) @mark.asyncio @@ -949,9 +883,7 @@ async def test_find_elements(setup_functional_environment): locator_type = By.XPATH locator_value = "//input" - elements = synchronous.find_elements( - server_url, session, locator_type, locator_value - ) + elements = synchronous.find_elements(server_url, session, locator_type, locator_value) async_elements = await asynchronous.find_elements( server_url, session, locator_type, locator_value ) @@ -970,9 +902,7 @@ async def test_find_element_fails_when_invalid_data_input(setup_functional_envir synchronous.find_element(server_url, session, locator_type, locator_value) with raises(WebDriverError): - await asynchronous.find_element( - server_url, session, locator_type, locator_value - ) + await asynchronous.find_element(server_url, session, locator_type, locator_value) @mark.asyncio @@ -981,14 +911,9 @@ async def test_find_element(setup_functional_environment): locator_type = By.XPATH locator_value = "//input" + assert synchronous.find_element(server_url, session, locator_type, locator_value) is not None assert ( - synchronous.find_element(server_url, session, locator_type, locator_value) - is not None - ) - assert ( - await asynchronous.find_element( - server_url, session, locator_type, locator_value - ) + await asynchronous.find_element(server_url, session, locator_type, locator_value) is not None ) @@ -1005,9 +930,7 @@ async def test_get_property(setup_functional_environment): synchronous.send_keys(server_url, session, element, text) assert synchronous.get_property(server_url, session, element, property) == text - assert ( - await asynchronous.get_property(server_url, session, element, property) == text - ) + assert await asynchronous.get_property(server_url, session, element, property) == text @mark.asyncio @@ -1033,9 +956,7 @@ async def test_send_keys(setup_functional_environment): element = synchronous.find_element(server_url, session, locator_type, locator_value) - assert ( - await asynchronous.send_keys(server_url, session, element, text_async) is True - ) + assert await asynchronous.send_keys(server_url, session, element, text_async) is True assert synchronous.send_keys(server_url, session, element, text_sync) is True diff --git a/tests/integration/test_async_scenarios.py b/tests/integration/test_async_scenarios.py index d9ba381..f3dc302 100644 --- a/tests/integration/test_async_scenarios.py +++ b/tests/integration/test_async_scenarios.py @@ -2,7 +2,6 @@ from pytest import mark - @mark.asyncio async def test_get_all_links(setup_functional_environment): server_url, session = setup_functional_environment @@ -12,11 +11,6 @@ async def test_get_all_links(setup_functional_environment): for i in range(4): i += 1 locator_value = f"//a[@id='a{i}']" - anchor = synchronous.find_element( - server_url, session, locator_type, locator_value - ) + anchor = synchronous.find_element(server_url, session, locator_type, locator_value) anchors.append(anchor) - assert ( - await asynchronous.get_text(server_url, session, anchors[i - 1]) - == f"any{i}.com" - ) + assert await asynchronous.get_text(server_url, session, anchors[i - 1]) == f"any{i}.com" diff --git a/tests/performance/README.md b/tests/performance/README.md new file mode 100644 index 0000000..26b0e3f --- /dev/null +++ b/tests/performance/README.md @@ -0,0 +1,858 @@ +# Scenario 1 - No concurrence +## Execution 1 +### No shared session +```bash +python -m pytest -k test_big_scenario_of_functions_without_session_http --durations=0 -random-order +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=1382528414 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collected 301 items / 291 deselected / 10 selected + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 PASSED [ 10%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 PASSED [ 20%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 PASSED [ 30%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 PASSED [ 40%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 PASSED [ 50%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 PASSED [ 60%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 PASSED [ 70%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 PASSED [ 80%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 PASSED [ 90%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 PASSED [100%] + +====================================================== slowest durations ======================================================= +3.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +2.23s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.84s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.78s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +1.78s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.77s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.73s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.70s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.55s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +============================================= 10 passed, 291 deselected in 28.95s ============================================== + +``` + +## Shared session +```bash +python -m pytest -k test_big_scenario_of_functions_with_session_http --durations=0 -random-order +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=2805815262 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collected 301 items / 291 deselected / 10 selected + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 PASSED [ 10%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 PASSED [ 20%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 PASSED [ 30%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 PASSED [ 40%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 PASSED [ 50%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 PASSED [ 60%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 PASSED [ 70%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 PASSED [ 80%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 PASSED [ 90%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 PASSED [100%] + +====================================================== slowest durations ======================================================= +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.65s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.63s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.54s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.54s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +1.50s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.49s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.45s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.40s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.38s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.59s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +============================================= 10 passed, 291 deselected in 24.69s ============================================== +``` + +## Execution 2 +### No shared session +```bash +python -m pytest -k test_big_scenario_of_functions_without_session_http --durations=0 -random-order +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=55218217 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collected 301 items / 291 deselected / 10 selected + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 PASSED [ 10%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 PASSED [ 20%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 PASSED [ 30%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 PASSED [ 40%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 PASSED [ 50%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 PASSED [ 60%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 PASSED [ 70%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 PASSED [ 80%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 PASSED [ 90%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 PASSED [100%] + +====================================================== slowest durations ======================================================= +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +2.01s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.86s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.84s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.73s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.65s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.61s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.59s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.58s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +1.53s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.27s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.61s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.60s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.57s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.53s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +============================================= 10 passed, 291 deselected in 26.84s ============================================== + +``` +### Shared session +```bash +python -m pytest -k test_big_scenario_of_functions_with_session_http --durations=0 -random-order +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=1872312787 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collected 301 items / 291 deselected / 10 selected + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 PASSED [ 10%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 PASSED [ 20%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 PASSED [ 30%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 PASSED [ 40%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 PASSED [ 50%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 PASSED [ 60%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 PASSED [ 70%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 PASSED [ 80%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 PASSED [ 90%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 PASSED [100%] + +====================================================== slowest durations ======================================================= +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.91s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.61s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.47s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +1.46s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.52s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.51s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +============================================= 10 passed, 291 deselected in 25.11s ============================================== +``` + +## Execution 3 +### No shared session +```bash +python -m pytest -k test_big_scenario_of_functions_without_session_http --durations=0 -random-order +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=299171876 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collected 301 items / 291 deselected / 10 selected + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 PASSED [ 10%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 PASSED [ 20%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 PASSED [ 30%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 PASSED [ 40%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 PASSED [ 50%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 PASSED [ 60%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 PASSED [ 70%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 PASSED [ 80%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 PASSED [ 90%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 PASSED [100%] + +====================================================== slowest durations ======================================================= +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.72s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +1.67s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.66s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.50s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.46s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.32s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.63s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +============================================= 10 passed, 291 deselected in 25.62s ============================================== +``` +### Shared session +```bash +python -m pytest -k test_big_scenario_of_functions_with_session_http --durations=0 -random-order +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=874562232 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collected 301 items / 291 deselected / 10 selected + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 PASSED [ 10%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 PASSED [ 20%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 PASSED [ 30%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 PASSED [ 40%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 PASSED [ 50%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 PASSED [ 60%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 PASSED [ 70%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 PASSED [ 80%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 PASSED [ 90%] +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 PASSED [100%] + +====================================================== slowest durations ======================================================= +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.67s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.60s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.47s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.28s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.49s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +============================================= 10 passed, 291 deselected in 24.23s ============================================== +``` +# Secenario 2 - with concurrence (-n auto) +## Execution 1 +### Shared session +```bash +python -m pytest -k test_big_scenario_of_functions_with_session_http --durations=0 -random-order -n auto +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=4074239062 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +4 workers [10 items] +scheduling tests via LoadScheduling + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +[gw3] [ 10%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +[gw1] [ 20%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +[gw2] [ 30%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +[gw0] [ 40%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +[gw3] [ 50%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +[gw2] [ 60%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +[gw0] [ 70%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +[gw1] [ 80%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +[gw3] [ 90%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +[gw1] [100%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 + +====================================================== slowest durations ======================================================= +3.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +3.75s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +3.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.60s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.59s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +3.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.41s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +3.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +2.23s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +2.14s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +2.12s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +2.12s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.72s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.31s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.30s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.17s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.08s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.04s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.82s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.51s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +===================================================== 10 passed in 18.04s ====================================================== +``` +### No shared session +```bash +python -m pytest -k test_big_scenario_of_functions_without_session_http --durations=0 -random-order -n auto +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=1525082352 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +4 workers [10 items] +scheduling tests via LoadScheduling + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +[gw0] [ 10%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +[gw1] [ 20%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +[gw2] [ 30%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +[gw3] [ 40%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +[gw1] [ 50%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +[gw0] [ 60%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +[gw2] [ 70%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +[gw3] [ 80%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +[gw1] [ 90%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +[gw0] [100%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 + +====================================================== slowest durations ======================================================= +3.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.63s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +3.60s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.58s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +3.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.47s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.34s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +3.33s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +3.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +3.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +2.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +2.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +2.38s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +2.31s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.93s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.85s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.66s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.64s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.59s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.54s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.94s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.91s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.13s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.11s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.11s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +===================================================== 10 passed in 18.93s ====================================================== + +``` + +## Execution 2 +### Shared session +```bash +python -m pytest -k test_big_scenario_of_functions_with_session_http --durations=0 -random-order -n auto +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=3270874462 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +4 workers [10 items] +scheduling tests via LoadScheduling + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +[gw2] [ 10%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +[gw0] [ 20%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +[gw1] [ 30%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +[gw3] [ 40%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +[gw0] [ 50%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +[gw2] [ 60%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +[gw3] [ 70%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +[gw1] [ 80%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +[gw0] [ 90%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +[gw2] [100%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 + +====================================================== slowest durations ======================================================= +3.81s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +3.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.65s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +3.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +3.55s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +3.36s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +3.32s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.32s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.12s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +2.70s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +2.63s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +2.39s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +2.29s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.90s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.37s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.34s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.85s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.85s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.17s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.13s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.10s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +===================================================== 10 passed in 18.89s ====================================================== + +``` +### No shared session +```bash +python -m pytest -k test_big_scenario_of_functions_without_session_http --durations=0 -random-order -n auto +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=1413430422 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +4 workers [10 items] +scheduling tests via LoadScheduling + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +[gw2] [ 10%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +[gw1] [ 20%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +[gw3] [ 30%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +[gw0] [ 40%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +[gw0] [ 50%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +[gw2] [ 60%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +[gw1] [ 70%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +[gw3] [ 80%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +[gw2] [ 90%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +[gw1] [100%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 + +====================================================== slowest durations ======================================================= +3.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.46s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.45s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +3.37s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +3.33s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +2.49s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +2.39s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +2.29s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +2.29s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.93s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.90s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.56s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.55s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.55s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.36s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.77s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.75s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.12s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +===================================================== 10 passed in 18.52s ====================================================== + +``` + +## Execution 3 +### Shared session +```bash +python -m pytest -k test_big_scenario_of_functions_with_session_http --durations=0 -random-order -n auto +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=700748289 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +4 workers [10 items] +scheduling tests via LoadScheduling + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +[gw0] [ 10%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +[gw3] [ 20%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +[gw2] [ 30%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +[gw1] [ 40%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +[gw2] [ 50%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +[gw0] [ 60%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +[gw3] [ 70%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +[gw1] [ 80%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +[gw0] [ 90%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +[gw3] [100%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 + +====================================================== slowest durations ======================================================= +3.86s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +3.82s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +3.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.61s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.57s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +3.35s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.26s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.24s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +2.59s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +2.52s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +2.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +2.34s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.90s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.89s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.77s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.74s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.93s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.86s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.19s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.12s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +===================================================== 10 passed in 19.41s ====================================================== +``` +### No shared session +```bash +python -m pytest -k test_big_scenario_of_functions_without_session_http --durations=0 -random-order -n auto +===================================================== test session starts ====================================================== +platform linux -- Python 3.12.3, pytest-9.0.1, pluggy-1.6.0 +Using --randomly-seed=3110200315 +rootdir: /home/douglas/repo/caqui +configfile: pytest.ini +plugins: xdist-3.8.0, randomly-4.0.1, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +4 workers [10 items] +scheduling tests via LoadScheduling + +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +[gw3] [ 10%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +[gw2] [ 20%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +[gw0] [ 30%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +[gw1] [ 40%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +[gw3] [ 50%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +[gw2] [ 60%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +[gw0] [ 70%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +[gw1] [ 80%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +[gw3] [ 90%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +[gw2] [100%] PASSED tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 + +====================================================== slowest durations ======================================================= +3.67s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +3.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.54s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.54s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.45s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +3.38s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.10s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +2.52s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +2.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +2.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +2.26s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.93s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.51s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.39s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.22s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.86s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.85s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.10s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +===================================================== 10 passed in 18.60s ====================================================== + +``` + +# Data processing +```bash +python -m pytest -k process_data -s +``` +# Result +- In scenarios without concurrence all executions using shared session (code bellow) performed better +```python + async with ClientSession() as session_http: + page = AsyncPage(server_url, capabilities, PAGE_URL, session_http=session_http) +``` +- In scenarios with concurrent (`pytest -n auto`) the results were almost the same for shared and non-shared session + +```bash +# Data organized by lowest 'duration' +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.80, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 28.63, 'call': 17.86, 'setup': 7.11, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +``` + +```bash +# Data organized by lowest accumulated duration (sum the duration of all threads) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.70, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.00, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.90, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'call': 32.10, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.90, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.90, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +``` + +# Tuning web driver +TO run all tests do `python -m pytest -k TestPerformance`. All results are ordered by lowest duration. +## Scenario 1 - no aditional arguments in driver +- Execution 1 (duration): 64.65s +- Execution 2 (duration): 66.40s +- Execution 3 (duration): 67.16s + +## Scenario 2 - added arguments to driver +```python +options = ChromeOptionsBuilder().args([ + "headless", + "blink-settings=imagesEnabled=false", + "disable-extensions", + "disable-plugins", + "disable-background-timer-throttling" + ]) +# and page_load_strategy("eager") +capabilities = ( + ChromeCapabilitiesBuilder() + .accept_insecure_certs(True) + .add_options(options) + .page_load_strategy("eager") +``` +- Execution 3 (duration): 46.42s +- Execution 1 (duration): 47.54s +- Execution 2 (duration): 48.12s + +## Scenario 3 - reuse server instance +Not dispose the server after finishe the test +```python +@fixture(autouse=True, scope="session") +def setup_server(): + server = Server.get_instance(port=SERVER_PORT) + server.start() + # yield + # server.dispose(delay=3) +``` +- Execution 3 (duration): 42.80s +- Execution 2 (duration): 42.93s +- Execution 1 (duration): 43.27s + +## Scenario 4 - run in multiprocessing +```python +# add -n auto +python -m pytest -k TestPerformance -n auto +``` +- Execution 2 (duration): 28.19s +- Execution 3 (duration): 28.53s +- Execution 1 (duration): 29.96s + +## Scenario 5 - using `ujson` module instead of built-in `json` +Executed with a fresh server instance +- Execution 1 (duration): 27.49s +- Execution 2 (duration): 27.68s +- Execution 3 (duration): 28.44s +- mean: 27.87s + +## Scenario 6 - using `orjson` module +Executed with a fresh server instance +- Execution 1 (duration): 27.57s +- Execution 3 (duration): 27.90s +- Execution 2 (duration): 28.11s +- mean: 27.86s + +## Scenario 7 - using `urllib3` module instead of `requests` +- Execution 1 (duration): 27.84s +- Execution 2 (duration): 34.95s +- Execution 3 (duration): 28.32s \ No newline at end of file diff --git a/tests/performance/test_process_data.py b/tests/performance/test_process_data.py new file mode 100644 index 0000000..052f154 --- /dev/null +++ b/tests/performance/test_process_data.py @@ -0,0 +1,463 @@ +import re +from collections import OrderedDict + +DATA = [ + { + "title": "# Scenario 1 | No concurrence | Execution 1 | No Shared session\n", + "output": """ +3.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +2.23s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.84s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.78s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +1.78s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.77s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.73s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.70s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.55s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +""", + }, + { + "title": "# Scenario 1 | No concurrence | Execution 1 | Shared session\n", + "output": """ +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.65s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.63s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.54s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.54s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +1.50s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.49s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.45s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.40s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.38s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.59s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +""", + }, + { + "title": "# Scenario 1 | No concurrence | Execution 2 | No Shared session\n", + "output": """ +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +2.01s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.86s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.84s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.73s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.65s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.61s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.59s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.58s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +1.53s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.27s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.61s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.60s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.57s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.53s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 + """, + }, + { + "title": "# Scenario 1 | No concurrence | Execution 2 | Shared session\n", + "output": """ +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.91s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.61s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.47s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +1.46s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.52s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.51s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 + """, + }, + { + "title": "# Scenario 1 | No concurrence | Execution 3 | No Shared session\n", + "output": """ +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.72s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +1.67s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.66s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.50s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.46s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.32s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.63s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 + """, + }, + { + "title": "# Scenario 1 | No concurrence | Execution 3 | Shared session\n", + "output": """ +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.67s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.60s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.47s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +1.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.28s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.49s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.44s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.42s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.41s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 + """, + }, + { + "title": "# Scenario 2 | With concurrence | Execution 1 | Shared session\n", + "output": """ +3.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +3.75s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +3.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.60s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.59s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +3.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.41s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +3.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +2.23s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +2.14s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +2.12s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +2.12s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.72s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.31s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.30s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +1.17s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.08s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +1.04s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.82s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.51s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 + """, + }, + { + "title": "# Scenario 2 | With concurrence | Execution 1 | No Shared session\n", + "output": """ +3.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.63s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +3.60s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.58s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +3.48s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.47s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.34s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +3.33s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +3.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +3.06s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +2.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +2.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +2.38s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +2.31s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +1.93s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +1.85s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.66s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.64s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.59s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.54s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.94s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.91s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.13s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.11s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.11s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 + """, + }, + { + "title": "# Scenario 2 | With concurrence | Execution 2 | Shared session\n", + "output": """ +3.81s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +3.69s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.65s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +3.62s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +3.55s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +3.36s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +3.32s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.32s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.12s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +2.70s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +2.63s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +2.39s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +2.29s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.90s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.47s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.37s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.34s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.85s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.85s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.17s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.13s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +0.10s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 + """, + }, + { + "title": "# Scenario 2 | With concurrence | Execution 2 | No Shared session\n", + "output": """ +3.51s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.46s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.45s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.44s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +3.37s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +3.33s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +2.49s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +2.39s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +2.29s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +2.29s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.93s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.90s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.56s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +1.55s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.55s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.36s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +0.77s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.75s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.12s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +0.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 + """, + }, + { + "title": "# Scenario 2 | With concurrence | Execution 3 | Shared session\n", + "output": """ +3.86s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +3.82s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +3.79s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +3.61s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +3.57s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +3.35s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +3.26s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +3.24s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +3.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +2.59s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +2.52s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 +2.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +2.34s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +1.90s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +1.89s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +1.77s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +1.74s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http3 +1.45s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http9 +0.93s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http4 +0.86s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http8 +0.19s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http10 +0.15s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http7 +0.12s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http1 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http5 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http6 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_with_session_http2 + """, + }, + { + "title": "# Scenario 2 | With concurrence | Execution 3 | No Shared session\n", + "output": """ +3.67s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +3.56s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.54s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.54s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +3.45s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +3.43s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +3.38s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +3.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +3.10s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +3.07s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +2.52s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +2.43s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +2.40s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +2.26s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 +1.93s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +1.88s call tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +1.51s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +1.46s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http8 +1.39s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http5 +1.22s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.86s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http6 +0.85s setup tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http2 +0.14s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http10 +0.10s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http4 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http7 +0.09s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http1 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http9 +0.08s teardown tests/performance/test_single_session_http.py::test_big_scenario_of_functions_without_session_http3 + """, + }, +] + + +def test_convert_to_csv(): + FILE = "data-processed.txt" + for item in DATA: + data = item["output"] + data = data.split("\n") + # format data in csv + result = [] + for row in data: + # remove seconds (s) from duration + row = row.replace("s ", " ") + # convert spaces to comma + row = re.sub(r"\s+", ",", row) + result.append(row) + + result_dict = OrderedDict() + for row in result: + if not row: + continue + new = row.split(",") + if not new[0]: + continue + duration = float(new[0]) + stage = new[1] + # sum duration of stages + result_dict[stage] = round(result_dict.get(stage, 0) + duration, 2) + result_dict["duration"] = result_dict.get("duration", 0) + duration + result_dict["title"] = item["title"] + result_dict[stage] = round(result_dict[stage], 2) + result_dict["duration"] = round(result_dict["duration"], 2) + # sample: {'setup': 7.11, 'teardown': 3.66, 'call': 17.86} + print(result_dict) + with open(FILE, "a") as f: + f.write(f"{str(result_dict)}\n") + with open(FILE, "a") as f: + f.write("=====\n") + print(f"Data saved to {FILE}") diff --git a/tests/performance/test_single_session_http.py b/tests/performance/test_single_session_http.py new file mode 100644 index 0000000..d7ced8c --- /dev/null +++ b/tests/performance/test_single_session_http.py @@ -0,0 +1,278 @@ +from aiohttp import ClientSession +from pytest import mark +from caqui import synchronous +from caqui.by import By +from caqui.easy import AsyncPage +from tests.constants import PAGE_URL +from pytest import mark +import pytest_asyncio +from caqui.easy.capabilities import ChromeCapabilitiesBuilder +from caqui.easy.options import ChromeOptionsBuilder + +SERVER_PORT = 9999 +SERVER_URL = f"http://localhost:{SERVER_PORT}" +CAPTURES = "captures" +LOAD = 10 # requests + +# @mark.skip(reason="Used for performance tests") +class TestPerformance: + def _build_capabilities(self): + options = ChromeOptionsBuilder().args([ + "headless", + "blink-settings=imagesEnabled=false", + "disable-extensions", + "disable-plugins", + "disable-background-timer-throttling" + ]) + capabilities = ( + ChromeCapabilitiesBuilder() + .accept_insecure_certs(True) + .add_options(options) + .page_load_strategy("eager") + ).to_dict() + return capabilities + + async def _body(self,page): + await page.implicitly_wait(10) + await page.get( + PAGE_URL, + ) + for _ in range(LOAD): + click_button = await page.find_element(By.ID, "button") + await click_button.click() + + await page.switch_to.active_element.get_attribute("value") + element = await page.find_element(By.XPATH, "//a") + # Returns and base64 encoded string into image + await element.screenshot("/tmp/image.png") + + await page.back() + await page.forward() + await page.refresh() + + alert_element = await page.find_element(By.CSS_SELECTOR, "#alert-button-prompt") + await alert_element.click() + alert_object = page.switch_to.alert + await page.alert.accept() + + await alert_element.click() + await alert_object.send_keys("Caqui") + await alert_object.dismiss() + + iframe = await page.find_element(By.ID, "my-iframe") + # switch to selected iframe + await page.switch_to.frame(iframe) + await page.switch_to.default_content() + # switching to second iframe based on index + iframe = (await page.find_elements(By.ID, "my-iframe"))[0] + + # switch to selected iframe + await page.switch_to.frame(iframe) + # switch back to default content + await page.switch_to.default_content() + + window_handle = page.current_window_handle + assert len(page.window_handles) >= 1 + await page.switch_to.window(window_handle) + # Opens a new tab and switches to new tab + await page.switch_to.new_window("tab") + # Opens a new window and switches to new window + await page.switch_to.new_window("window") + + # Access each dimension individually + await page.set_window_size(1024, 768) + # Move the window to the top left of the primary monitor + await page.set_window_position(0, 0) + await page.maximize_window() + # await driver.minimize_window() # does not work on headless mode + await page.save_screenshot("/tmp/image.png") + + # Executing JavaScript to capture innerText of header element + await page.execute_script('alert("any warn")') + + @pytest_asyncio.fixture + async def setup_environment_without_session_http(self): + server_url = SERVER_URL + capabilities = self._build_capabilities() + page = AsyncPage(server_url, capabilities, PAGE_URL) + yield page + try: + synchronous.dismiss_alert(server_url, page.session) + except Exception: + pass + finally: + page.quit() + + @pytest_asyncio.fixture + async def setup_environment_with_session_http(self): + server_url = SERVER_URL + capabilities = self._build_capabilities() + async with ClientSession() as session_http: + page = AsyncPage(server_url, capabilities, PAGE_URL, session_http=session_http) + yield page + try: + synchronous.dismiss_alert(server_url, page.session) + except Exception: + pass + finally: + page.quit() + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http1( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http1( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http2( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http2( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http3( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http3( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http4( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http4( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http5( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http5( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http6( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http6( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http7( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http7( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http8( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http8( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http9( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http9( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_with_session_http10( + self, + setup_environment_with_session_http: AsyncPage, + ): + page = setup_environment_with_session_http + await self._body(page) + + @mark.asyncio + async def test_big_scenario_of_functions_without_session_http10( + self, + setup_environment_without_session_http: AsyncPage, + ): + page = setup_environment_without_session_http + await self._body(page) diff --git a/tests/unit/test_async_unit.py b/tests/unit/test_async_unit.py index daafc89..43e52f6 100644 --- a/tests/unit/test_async_unit.py +++ b/tests/unit/test_async_unit.py @@ -18,7 +18,7 @@ async def test_get_rect(): async def mock_request(*args, **kwargs): return fake_responses.GET_RECT - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_rect("", "", "") == expected @@ -27,7 +27,7 @@ async def test_actions_scroll_to_element(): async def mock_request(*args, **kwargs): return fake_responses.ACTIONS - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.actions_scroll_to_element("", "", "") is True @@ -36,7 +36,7 @@ async def test_submit(): async def mock_request(*args, **kwargs): return fake_responses.CLICK - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.submit("", "", "") is True @@ -45,7 +45,7 @@ async def test_actions_click(): async def mock_request(*args, **kwargs): return fake_responses.ACTIONS - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.actions_click("", "", "") is True @@ -54,7 +54,7 @@ async def test_set_timeouts(): async def mock_request(*args, **kwargs): return fake_responses.GET_TIMEOUTS - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.set_timeouts("", "", "") is True @@ -65,7 +65,7 @@ async def test_find_children_elements(): async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENTS - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert element in await asynchronous.find_children_elements("", "", "", "", "") @@ -76,7 +76,7 @@ async def test_find_child_element(): async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENT - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.find_child_element("", "", "", "", "") == element @@ -87,7 +87,7 @@ async def test_execute_script(): async def mock_request(*args, **kwargs): return fake_responses.EXECUTE_SCRIPT - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.execute_script("", "", "", "") == expected @@ -98,7 +98,7 @@ async def test_get_page_source(): async def mock_request(*args, **kwargs): return fake_responses.GET_PAGE_SOURCE - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert expected in await asynchronous.get_page_source("", "") @@ -109,7 +109,7 @@ async def test_get_alert_text(): async def mock_request(*args, **kwargs): return fake_responses.GET_ALERT_TEXT - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_alert_text("", "") == expected @@ -120,7 +120,7 @@ async def test_get_active_element(): async def mock_request(*args, **kwargs): return fake_responses.GET_ACTIVE_ELEMENT - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_active_element("", "") == expected @@ -129,7 +129,7 @@ async def test_clear_element(): async def mock_request(*args, **kwargs): return fake_responses.CLEAR_ELEMENT - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.clear_element("", "", "") is True @@ -138,7 +138,7 @@ async def test_is_element_enabled(): async def mock_request(*args, **kwargs): return fake_responses.IS_ELEMENT_ENABLED - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.is_element_enabled("", "", "") is True @@ -149,7 +149,7 @@ async def test_get_css_value(): async def mock_request(*args, **kwargs): return fake_responses.GET_CSS_COLOR_VALUE - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_css_value("", "", "", "") == expected @@ -158,7 +158,7 @@ async def test_is_element_selected(): async def mock_request(*args, **kwargs): return fake_responses.IS_ELEMENT_SELECTED - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.is_element_selected("", "", "") is False @@ -169,7 +169,7 @@ async def test_get_window_rectangle(): async def mock_request(*args, **kwargs): return fake_responses.GET_WINDOW_RECTANGLE - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert expected in await asynchronous.get_window_rectangle("", "") @@ -180,7 +180,7 @@ async def test_get_window_handles(): async def mock_request(*args, **kwargs): return fake_responses.GET_WINDOW_HANDLES - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_window_handles("", "") == expected @@ -191,7 +191,7 @@ async def test_close_window(): async def mock_request(*args, **kwargs): return fake_responses.CLOSE_WINDOW - with patch("caqui.asynchronous.__delete", mock_request): + with patch("caqui.asynchronous._delete", mock_request): assert await asynchronous.close_window("", "") == expected @@ -202,13 +202,13 @@ async def test_get_window(): async def mock_request(*args, **kwargs): return fake_responses.GET_WINDOW - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_window("", "") == expected @mark.asyncio async def test_go_back(): - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.go_back("", "") is True @@ -219,7 +219,7 @@ async def test_get_property(): async def mock_request(*args, **kwargs): return fake_responses.GET_PROPERTY_VALUE - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_property("", "", "", "") == expected @@ -230,7 +230,7 @@ async def test_get_attribute(): async def mock_request(*args, **kwargs): return fake_responses.GET_ATTRIBUTE_VALUE - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_attribute("", "", "", "") == expected @@ -241,7 +241,7 @@ async def test_get_url(): async def mock_request(*args, **kwargs): return fake_responses.GET_URL - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): response = await asynchronous.get_url("", "") assert expected in response @@ -253,7 +253,7 @@ async def test_get_timeouts(): async def mock_request(*args, **kwargs): return fake_responses.GET_TIMEOUTS - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): response = await asynchronous.get_timeouts("", "") assert expected in response @@ -263,7 +263,7 @@ async def test_get_status(): async def mock_request(*args, **kwargs): return fake_responses.GET_STATUS - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): response = await asynchronous.get_status("") assert response.get("value").get("ready") is True @@ -275,7 +275,7 @@ async def test_get_title(): async def mock_request(*args, **kwargs): return fake_responses.GET_TITLE - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_title("", "") == expected @@ -286,7 +286,7 @@ async def test_get_cookies(): async def mock_request(*args, **kwargs): return fake_responses.GET_COOKIES - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_cookies("", "") == expected @@ -297,31 +297,31 @@ async def test_get_text(): async def mock_request(*args, **kwargs): return fake_responses.GET_TEXT - with patch("caqui.asynchronous.__get", mock_request): + with patch("caqui.asynchronous._get", mock_request): assert await asynchronous.get_text("", "", "") == expected @mark.asyncio async def test_close_session(): - with patch("caqui.asynchronous.__delete", mock_request): + with patch("caqui.asynchronous._delete", mock_request): assert await asynchronous.close_session("", "") is True @mark.asyncio async def test_go_to_page(): - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.go_to_page("", "", "") is True @mark.asyncio async def test_send_keys(): - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.send_keys("", "", "", "") is True @mark.asyncio async def test_click(): - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.click("", "", "") is True @@ -332,7 +332,7 @@ async def test_find_elements(): async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENTS - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert element in await asynchronous.find_elements("", "", "", "") @@ -343,7 +343,7 @@ async def test_find_element(): async def mock_request(*args, **kwargs): return fake_responses.FIND_ELEMENT - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.find_element("", "", "", "") == element @@ -354,5 +354,5 @@ async def test_get_session(): async def mock_request(*args, **kwargs): return fake_responses.GET_SESSION - with patch("caqui.asynchronous.__post", mock_request): + with patch("caqui.asynchronous._post", mock_request): assert await asynchronous.get_session(server_url="", capabilities={}) == expected diff --git a/tests/unit/test_sync_unit.py b/tests/unit/test_sync_unit.py index 570d4d2..74f83e3 100644 --- a/tests/unit/test_sync_unit.py +++ b/tests/unit/test_sync_unit.py @@ -3,33 +3,33 @@ from tests import fake_responses -@patch("requests.request", return_value=fake_responses.GET_RECT) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_RECT) def test_get_rect(*args): expected = {"height": 23, "width": 183, "x": 10, "y": 9652.12} assert synchronous.get_rect("", "", "") == expected -@patch("requests.request", return_value=fake_responses.ACTIONS) +@patch("caqui.synchronous.request", return_value=fake_responses.ACTIONS) def test_actions_scroll_to_element(*args): assert synchronous.actions_scroll_to_element("", "", "") is True -@patch("requests.request", return_value=fake_responses.CLICK) +@patch("caqui.synchronous.request", return_value=fake_responses.CLICK) def test_submit(*args): assert synchronous.submit("", "", "") is True -@patch("requests.request", return_value=fake_responses.ACTIONS) +@patch("caqui.synchronous.request", return_value=fake_responses.ACTIONS) def test_actions_click(*args): assert synchronous.actions_click("", "", "") is True -@patch("requests.request", return_value=fake_responses.GET_TIMEOUTS) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_TIMEOUTS) def test_set_timeouts(*args): assert synchronous.set_timeouts("", "", "") is True -@patch("requests.request", return_value=fake_responses.FIND_ELEMENTS) +@patch("caqui.synchronous.request", return_value=fake_responses.FIND_ELEMENTS) def test_find_children_elements(*args): element = "C230605181E69CB2C4C36B8E83FE1245_element_2" @@ -39,121 +39,121 @@ def test_find_children_elements(*args): assert len(elements) == 3 -@patch("requests.request", return_value=fake_responses.FIND_ELEMENT) +@patch("caqui.synchronous.request", return_value=fake_responses.FIND_ELEMENT) def test_find_child_element(*args): expected = "0.8851292311864847-1" assert synchronous.find_child_element("", "", "", "", "") == expected -@patch("requests.request", return_value=fake_responses.EXECUTE_SCRIPT) +@patch("caqui.synchronous.request", return_value=fake_responses.EXECUTE_SCRIPT) def test_execute_script(*args): expected = "any" assert synchronous.execute_script("", "", "", "") == expected -@patch("requests.request", return_value=fake_responses.GET_PAGE_SOURCE) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_PAGE_SOURCE) def test_get_page_source(*args): expected = "Sample page" assert expected in synchronous.get_page_source("", "") -@patch("requests.request", return_value=fake_responses.GET_ALERT_TEXT) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_ALERT_TEXT) def test_get_alert_text(*args): expected = "any warn" assert synchronous.get_alert_text("", "") == expected -@patch("requests.request", return_value=fake_responses.GET_ACTIVE_ELEMENT) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_ACTIVE_ELEMENT) def test_get_active_element(*args): expected = "0.8851292311864847-1" assert synchronous.get_active_element("", "") == expected -@patch("requests.request", return_value=fake_responses.CLEAR_ELEMENT) +@patch("caqui.synchronous.request", return_value=fake_responses.CLEAR_ELEMENT) def test_clear_element(*args): assert synchronous.clear_element("", "", "") is True -@patch("requests.request", return_value=fake_responses.IS_ELEMENT_ENABLED) +@patch("caqui.synchronous.request", return_value=fake_responses.IS_ELEMENT_ENABLED) def test_is_element_enabled(*args): assert synchronous.is_element_enabled("", "", "") is True -@patch("requests.request", return_value=fake_responses.GET_CSS_COLOR_VALUE) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_CSS_COLOR_VALUE) def test_get_css_value(*args): expected = "rgba(0, 0, 0, 1)" assert synchronous.get_css_value("", "", "", "") == expected -@patch("requests.request", return_value=fake_responses.IS_ELEMENT_SELECTED) +@patch("caqui.synchronous.request", return_value=fake_responses.IS_ELEMENT_SELECTED) def test_is_element_selected(*args): assert synchronous.is_element_selected("", "", "") is False -@patch("requests.request", return_value=fake_responses.GET_WINDOW_RECTANGLE) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_WINDOW_RECTANGLE) def test_get_window_rectangle(*args): expected = "height" assert expected in synchronous.get_window_rectangle("", "") -@patch("requests.request", return_value=fake_responses.GET_WINDOW_HANDLES) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_WINDOW_HANDLES) def test_get_window_handles(*args): expected = "2E55CCE389196328988ED244DAA52A5D" assert expected in synchronous.get_window_handles("", "") -@patch("requests.request", return_value=fake_responses.CLOSE_WINDOW) +@patch("caqui.synchronous.request", return_value=fake_responses.CLOSE_WINDOW) def test_close_window(*args): expected = [] assert synchronous.close_window("", "") == expected -@patch("requests.request", return_value=fake_responses.GET_WINDOW) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_WINDOW) def test_get_window(*args): expected = "845623CAE8115F2B60C9AE8596F13D94" assert expected in synchronous.get_window("", "") -@patch("requests.request", return_value=fake_responses.GET_URL) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_URL) def test_get_url(*args): expected = "playground.html" assert expected in synchronous.get_url("", "") -@patch("requests.request", return_value=fake_responses.GET_TIMEOUTS) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_TIMEOUTS) def test_get_timeouts(*args): expected = "implicit" assert expected in synchronous.get_timeouts("", "") -@patch("requests.request", return_value=fake_responses.GET_STATUS) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_STATUS) def test_get_status(*args): assert synchronous.get_status("").get("value").get("ready") is True -@patch("requests.request", return_value=fake_responses.GET_TITLE) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_TITLE) def test_get_title(*args): expected = "Sample page" assert synchronous.get_title("", "") == expected -@patch("requests.request", return_value=fake_responses.GET_COOKIES) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_COOKIES) def test_get_cookies(*args): expected = [] assert synchronous.get_cookies("", "") == expected -@patch("requests.request", return_value=fake_responses.FIND_ELEMENTS) +@patch("caqui.synchronous.request", return_value=fake_responses.FIND_ELEMENTS) def test_find_elements(*args): element = "C230605181E69CB2C4C36B8E83FE1245_element_2" @@ -163,55 +163,55 @@ def test_find_elements(*args): assert len(elements) == 3 -@patch("requests.request", return_value=fake_responses.GET_PROPERTY_VALUE) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_PROPERTY_VALUE) def test_get_property(*args): expected = "any_value" assert synchronous.get_property("", "", "", "") == expected -@patch("requests.request", return_value=fake_responses.GET_ATTRIBUTE_VALUE) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_ATTRIBUTE_VALUE) def test_get_attribute(*args): expected = "any_value" assert synchronous.get_attribute("", "", "", "") == expected -@patch("requests.request", return_value=fake_responses.GO_TO_PAGE) +@patch("caqui.synchronous.request", return_value=fake_responses.GO_TO_PAGE) def test_go_to_page(*args): assert synchronous.go_to_page("", "", "") is True -@patch("requests.request", return_value=fake_responses.CLOSE_SESSION) +@patch("caqui.synchronous.request", return_value=fake_responses.CLOSE_SESSION) def test_close_session(*args): assert synchronous.close_session("", "") is True -@patch("requests.request", return_value=fake_responses.GET_TEXT) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_TEXT) def test_get_text(*args): expected = "any" assert synchronous.get_text("", "", "") == expected -@patch("requests.request", return_value=fake_responses.SEND_KEYS) +@patch("caqui.synchronous.request", return_value=fake_responses.SEND_KEYS) def test_send_keys(*args): assert synchronous.send_keys("", "", "", "") is True -@patch("requests.request", return_value=fake_responses.CLICK) +@patch("caqui.synchronous.request", return_value=fake_responses.CLICK) def test_click(*args): assert synchronous.click("", "", "") is True -@patch("requests.request", return_value=fake_responses.GET_SESSION) +@patch("caqui.synchronous.request", return_value=fake_responses.GET_SESSION) def test_get_session(*args): expected = "4358a5b53794586af59678fc1653dc40" assert synchronous.get_session(server_url="", capabilities={}) == expected -@patch("requests.request", return_value=fake_responses.FIND_ELEMENT) +@patch("caqui.synchronous.request", return_value=fake_responses.FIND_ELEMENT) def test_find_element(*args): expected = "0.8851292311864847-1" From 0438c578291c5d2494df2c3a4f2d2dd8e793180b Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Sat, 29 Nov 2025 02:50:19 -0300 Subject: [PATCH 04/40] review readme --- tests/performance/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 26b0e3f..9698c50 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -847,12 +847,17 @@ Executed with a fresh server instance ## Scenario 6 - using `orjson` module Executed with a fresh server instance +Note: This was the choosen library as it has more starts in github, recent commits, a more liberative license and better/equal performance than `ujson` - Execution 1 (duration): 27.57s - Execution 3 (duration): 27.90s - Execution 2 (duration): 28.11s - mean: 27.86s ## Scenario 7 - using `urllib3` module instead of `requests` -- Execution 1 (duration): 27.84s -- Execution 2 (duration): 34.95s -- Execution 3 (duration): 28.32s \ No newline at end of file +Not planned. Need many refactoring and has gain for specific scenarios + +## Scenario 8 - convert code to CPython +- Execution 1 (duration): 27.57s +- Execution 3 (duration): 27.90s +- Execution 2 (duration): 28.11s +- mean: 27.86s From ca41d7ae8ce519035b345c16c52effea8bb30f63 Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Sat, 29 Nov 2025 14:47:01 -0300 Subject: [PATCH 05/40] fix issues in python3.7 --- caqui/easy/capabilities.py | 60 +++++++++++++++++++------------------- pyproject.toml | 4 ++- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/caqui/easy/capabilities.py b/caqui/easy/capabilities.py index 72ba3af..7c04295 100644 --- a/caqui/easy/capabilities.py +++ b/caqui/easy/capabilities.py @@ -1,5 +1,5 @@ from math import ceil - +from typing import Union from caqui.easy.options import BaseOptions @@ -21,7 +21,7 @@ class ProxyConfigurationBuilder: """ def __init__(self) -> None: - self.__proxy: dict = {} + self._proxy: dict = {} def proxy_type(self, proxy: str): """ @@ -31,8 +31,8 @@ def proxy_type(self, proxy: str): Reference: https://www.w3.org/TR/webdriver/#dfn-proxy-configuration """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "proxyType": proxy, } return self @@ -41,8 +41,8 @@ def proxy_autoconfig_url(self, url: str): """ Defines the URL for a proxy auto-config file if proxyType is equal to "pac". """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "proxyAutoconfigUrl": url, } return self @@ -53,8 +53,8 @@ def ftp_proxy(self, proxy: str): proxy: A host and optional port for scheme "ftp". """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "ftpProxy": proxy, } return self @@ -65,8 +65,8 @@ def http_proxy(self, proxy: str): proxy: A host and optional port for scheme "http". """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "httpProxy": proxy, } return self @@ -77,8 +77,8 @@ def no_proxy(self, proxies: list): proxies: A List containing any number of Strings. """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "noProxy": proxies, } return self @@ -89,8 +89,8 @@ def ssl_proxy(self, proxy: str): proxy: A host and optional port for scheme "https". """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "sslProxy": proxy, } return self @@ -101,8 +101,8 @@ def socks_proxy(self, proxy: str): proxy: A host and optional port with an undefined scheme. """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "socksProxy": proxy, } return self @@ -113,14 +113,14 @@ def socks_version(self, version: int): version: Any integer between 0 and 255 inclusive. """ - self.__proxy = { - **self.__proxy, + self._proxy = { + **self._proxy, "socksVersion": version, } return self def to_dict(self): - return {"proxy": self.__proxy} + return {"proxy": self._proxy} class TimeoutsBuilder: @@ -129,13 +129,13 @@ class TimeoutsBuilder: """ def __init__(self) -> None: - self.__timeouts: dict = {} + self._timeouts: dict = {} def implicit(self, timeout: int): """Notice: if the number is a float, converts it to an integer""" timeout = ceil(timeout) - self.__timeouts = { - **self.__timeouts, + self._timeouts = { + **self._timeouts, "implicit": timeout, } return self @@ -143,8 +143,8 @@ def implicit(self, timeout: int): def page_load(self, timeout: int): """Notice: if the number is a float, converts it to an integer""" timeout = ceil(timeout) - self.__timeouts = { - **self.__timeouts, + self._timeouts = { + **self._timeouts, "pageLoad": timeout, } return self @@ -152,14 +152,14 @@ def page_load(self, timeout: int): def script(self, timeout: int): """Notice: if the number is a float, converts it to an integer""" timeout = ceil(timeout) - self.__timeouts = { - **self.__timeouts, + self._timeouts = { + **self._timeouts, "script": timeout, } return self def to_dict(self): - return {"timeouts": self.__timeouts} + return {"timeouts": self._timeouts} class BaseCapabilities: @@ -219,7 +219,7 @@ def page_load_strategy(self, strategy: str): } return self - def proxy(self, proxy_configuration: dict | ProxyConfigurationBuilder): + def proxy(self, proxy_configuration: Union[dict, ProxyConfigurationBuilder]): """ Defines the current session’s proxy configuration. Use the ProxyConfigurationBuilder class for simplicity. @@ -242,7 +242,7 @@ def set_window_rect(self, decison: bool): } return self - def timeouts(self, session_timeouts: dict | TimeoutsBuilder): + def timeouts(self, session_timeouts: Union[dict , TimeoutsBuilder]): """ Describes the timeouts imposed on certain session operations. Use the TimeoutsBuilder class for simplicity. @@ -299,7 +299,7 @@ def user_agent(self, agent: str): } return self - def add_options(self, options: dict | BaseOptions): + def add_options(self, options: Union[dict , BaseOptions]): """Add vendor options, for example {"goog:chromeOptions": {"extensions": [], "args": ["--headless"]}} or {"moz:experimental-webdriver": true} diff --git a/pyproject.toml b/pyproject.toml index 7566698..d99ffef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,9 @@ classifiers = [ dependencies = [ "requests", "aiohttp", - "webdriver_manager" + "webdriver_manager", + "types-requests", + "orjson", ] [project.urls] From 4ed335be74a01f34d2fc324002f8a569e603170b Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Sat, 29 Nov 2025 15:25:52 -0300 Subject: [PATCH 06/40] Fix python 3.7 --- .gitignore | 2 + caqui/easy/capabilities.py | 4 +- caqui/helper.py | 4 +- caqui/synchronous.py | 8 +- data-processed.txt | 236 ++++++++++++++++++ setup.py | 26 ++ test-requirements.txt | 1 + tests/performance/test_single_session_http.py | 19 +- utils/generate-pyx-files.py | 49 ++++ 9 files changed, 331 insertions(+), 18 deletions(-) create mode 100644 data-processed.txt create mode 100644 setup.py create mode 100644 utils/generate-pyx-files.py diff --git a/.gitignore b/.gitignore index b87ea49..24c9fc1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ __pycache__/ # C extensions *.so +*.pyx +*.c # Distribution / packaging .Python diff --git a/caqui/easy/capabilities.py b/caqui/easy/capabilities.py index 7c04295..a5c90e6 100644 --- a/caqui/easy/capabilities.py +++ b/caqui/easy/capabilities.py @@ -242,7 +242,7 @@ def set_window_rect(self, decison: bool): } return self - def timeouts(self, session_timeouts: Union[dict , TimeoutsBuilder]): + def timeouts(self, session_timeouts: Union[dict, TimeoutsBuilder]): """ Describes the timeouts imposed on certain session operations. Use the TimeoutsBuilder class for simplicity. @@ -299,7 +299,7 @@ def user_agent(self, agent: str): } return self - def add_options(self, options: Union[dict , BaseOptions]): + def add_options(self, options: Union[dict, BaseOptions]): """Add vendor options, for example {"goog:chromeOptions": {"extensions": [], "args": ["--headless"]}} or {"moz:experimental-webdriver": true} diff --git a/caqui/helper.py b/caqui/helper.py index 5240f93..cecec9b 100644 --- a/caqui/helper.py +++ b/caqui/helper.py @@ -6,12 +6,12 @@ def save_picture(session, path, file_name, response): f.write(base64.b64decode((response))) -def get_elements(response): +def get_elements(response) -> list: values = response.get("value") return [list(value.values())[0] for value in values] -def get_element(response) -> dict: +def get_element(response) -> str: value = response.get("value") # Google Chrome element = value.get("ELEMENT") diff --git a/caqui/synchronous.py b/caqui/synchronous.py index 38bb3ed..5293db8 100644 --- a/caqui/synchronous.py +++ b/caqui/synchronous.py @@ -30,9 +30,7 @@ def _get(url): def _post(url, payload): try: - response = request( - "POST", url, headers=HEADERS, data= dumps(payload), timeout=60 - ) + response = request("POST", url, headers=HEADERS, data=dumps(payload), timeout=60) return _handle_response(response) except Exception as e: raise WebDriverError("'POST' request failed.") from e @@ -455,9 +453,7 @@ def find_child_element(server_url, session, parent_element, locator_type, locato response = _post(url, payload) return helper.get_element(response) except Exception as e: - raise WebDriverError( - f"Failed to find the child element from '{parent_element}'." - ) from e + raise WebDriverError(f"Failed to find the child element from '{parent_element}'.") from e def get_page_source(server_url, session) -> str: diff --git a/data-processed.txt b/data-processed.txt new file mode 100644 index 0000000..6a0b15d --- /dev/null +++ b/data-processed.txt @@ -0,0 +1,236 @@ +OrderedDict({'teardown': 3.67, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.66, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'call': 17.86, 'setup': 7.11, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) + +OrderedDict({'call': 32.73, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.81, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'call': 31.25, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.9, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +=====OrderedDict({'setup': 7.11, 'duration': 28.629999999999995, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.409999999999997, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.590000000000007, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.350000000000005, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.939999999999998, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.239999999999995, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.88999999999999, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52000000000001, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.90000000000001, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) + +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +===== +OrderedDict({'setup': 7.110000000000002, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.169999999999998, 'setup': 5.5600000000000005, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.620000000000001, 'setup': 5.5200000000000005, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.420000000000001, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.320000000000001, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.730000000000004, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.700000000000001, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.910000000000004, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.350000000000005, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.000000000000004, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.549999999999997, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.809999999999995, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.900000000000002, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.80, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 28.63, 'call': 17.86, 'setup': 7.11, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) + +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.70, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.00, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.90, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'call': 32.10, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.90, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.90, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..eee1001 --- /dev/null +++ b/setup.py @@ -0,0 +1,26 @@ +from distutils.core import setup +from Cython.Build import cythonize + +setup( + ext_modules=cythonize( + [ + "./caqui/asynchronous.pyx", + "./caqui/constants.pyx", + "./caqui/helper.pyx", + "./caqui/exceptions.pyx", + "./caqui/synchronous.pyx", + "./caqui/by.pyx", + "./caqui/__init__.pyx", + "./caqui/easy/capabilities.pyx", + "./caqui/easy/options.pyx", + "./caqui/easy/action_chains.pyx", + "./caqui/easy/alert.pyx", + "./caqui/easy/page.pyx", + "./caqui/easy/switch_to.pyx", + "./caqui/easy/window.pyx", + "./caqui/easy/element.pyx", + "./caqui/easy/__init__.pyx", + "./caqui/easy/server.pyx", + ] + ) +) diff --git a/test-requirements.txt b/test-requirements.txt index ec3406d..be27dee 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,4 @@ +cython pytest pytest-xdist selenium diff --git a/tests/performance/test_single_session_http.py b/tests/performance/test_single_session_http.py index d7ced8c..5e3a75b 100644 --- a/tests/performance/test_single_session_http.py +++ b/tests/performance/test_single_session_http.py @@ -14,16 +14,19 @@ CAPTURES = "captures" LOAD = 10 # requests + # @mark.skip(reason="Used for performance tests") class TestPerformance: def _build_capabilities(self): - options = ChromeOptionsBuilder().args([ - "headless", - "blink-settings=imagesEnabled=false", - "disable-extensions", - "disable-plugins", - "disable-background-timer-throttling" - ]) + options = ChromeOptionsBuilder().args( + [ + "headless", + "blink-settings=imagesEnabled=false", + "disable-extensions", + "disable-plugins", + "disable-background-timer-throttling", + ] + ) capabilities = ( ChromeCapabilitiesBuilder() .accept_insecure_certs(True) @@ -32,7 +35,7 @@ def _build_capabilities(self): ).to_dict() return capabilities - async def _body(self,page): + async def _body(self, page): await page.implicitly_wait(10) await page.get( PAGE_URL, diff --git a/utils/generate-pyx-files.py b/utils/generate-pyx-files.py new file mode 100644 index 0000000..dc81066 --- /dev/null +++ b/utils/generate-pyx-files.py @@ -0,0 +1,49 @@ +import os +import shutil +import subprocess + + +def convert_python_to_pyx(root_folder: str): + """ + Scans all subfolders of `root_folder` for .py files, + copies each next to the original file with a .pyx extension, + overwriting existing copies. + """ + result = [] + for current_path, _, files in os.walk(root_folder): + for filename in files: + if filename.endswith(".py"): + original_file = os.path.join(current_path, filename) + pyx_filename = filename[:-3] + ".pyx" # change extension + pyx_path = os.path.join(current_path, pyx_filename) + + # Copy and overwrite + shutil.copyfile(original_file, pyx_path) + result.append(pyx_path) + + content = f""" +from distutils.core import setup +from Cython.Build import cythonize +setup( + ext_modules = cythonize( + {result} + ) +) +""" + with open("setup.py", "w") as f: + f.write(content) + + +def build_pyx(): + # Run a simple command and capture output + result = subprocess.run( + ["python", "setup.py", "build_ext", "--inplace"], capture_output=True, text=True, check=True + ) + print("Stdout:", result.stdout) + print("Stderr:", result.stderr) + + +if __name__ == "__main__": + target_folder = "./caqui" + convert_python_to_pyx(target_folder) + build_pyx() From 177c8bc30a4fbdfbd55327a7f36130c9860b06db Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Sat, 29 Nov 2025 16:07:44 -0300 Subject: [PATCH 07/40] fix typing issues before build --- caqui/asynchronous.py | 196 +++++++++++------- caqui/easy/action_chains.py | 8 +- caqui/easy/capabilities.py | 4 +- caqui/easy/element.py | 4 +- caqui/easy/options.py | 2 +- caqui/easy/page.py | 6 +- caqui/synchronous.py | 2 +- data-processed.txt | 13 ++ ...nerate-pyx-files.py => build-pyx-files.py} | 0 utils/cleanup-cpython-files.py | 32 +++ 10 files changed, 182 insertions(+), 85 deletions(-) rename utils/{generate-pyx-files.py => build-pyx-files.py} (100%) create mode 100644 utils/cleanup-cpython-files.py diff --git a/caqui/asynchronous.py b/caqui/asynchronous.py index 7b6b4a6..42b20be 100644 --- a/caqui/asynchronous.py +++ b/caqui/asynchronous.py @@ -3,7 +3,7 @@ from caqui.constants import HEADERS from caqui.exceptions import WebDriverError from caqui.helper import save_picture, get_elements, get_element -from typing import Optional +from typing import Any, List, Optional, Union async def _handle_response(resp): @@ -21,7 +21,7 @@ async def _handle_response(resp): return result -async def _delete(url, session_http: ClientSession = None): +async def _delete(url, session_http: Union[ClientSession, None] = None): if session_http: try: async with session_http.delete(url, headers=HEADERS) as resp: @@ -38,7 +38,7 @@ async def _delete(url, session_http: ClientSession = None): raise WebDriverError("'DELETE' request failed.") from e -async def _post(url, payload, session_http: ClientSession = None): +async def _post(url, payload, session_http: Union[ClientSession, None] = None): if session_http: try: async with session_http.post(url, data=dumps(payload), headers=HEADERS) as resp: @@ -54,7 +54,7 @@ async def _post(url, payload, session_http: ClientSession = None): raise WebDriverError("'POST' request failed.") from e -async def _get(url, session_http: ClientSession = None): +async def _get(url, session_http: Union[ClientSession, None] = None): if session_http: try: async with session_http.get(url, headers=HEADERS) as resp: @@ -79,14 +79,16 @@ async def _handle_alert(server_url, session, command, session_http) -> bool: return True -async def _handle_window(server_url, session, command, session_http: ClientSession = None): +async def _handle_window( + server_url, session, command, session_http: Union[ClientSession, None] = None +): url = f"{server_url}/session/{session}/window/{command}" - payload = {} + payload: dict = {} await _post(url, payload, session_http=session_http) return True -async def add_cookie(server_url, session, cookie, session_http: ClientSession = None): +async def add_cookie(server_url, session, cookie, session_http: Union[ClientSession, None] = None): """Add cookie""" try: url = f"{server_url}/session/{session}/cookie" @@ -97,7 +99,7 @@ async def add_cookie(server_url, session, cookie, session_http: ClientSession = raise WebDriverError("Failed to add cookie.") from e -async def delete_cookie(server_url, session, name, session_http: ClientSession = None): +async def delete_cookie(server_url, session, name, session_http: Union[ClientSession, None] = None): """Delete cookie by name""" try: url = f"{server_url}/session/{session}/cookie/{name}" @@ -107,22 +109,22 @@ async def delete_cookie(server_url, session, name, session_http: ClientSession = raise WebDriverError(f"Failed to delete cookie '{name}'.") from e -async def refresh_page(server_url, session, session_http: ClientSession = None): +async def refresh_page(server_url, session, session_http: Union[ClientSession, None] = None): """Refresh page""" try: url = f"{server_url}/session/{session}/refresh" - payload = {} + payload: dict = {} await _post(url, payload, session_http=session_http) return True except Exception as e: raise WebDriverError("Failed to refresh page.") from e -async def go_forward(server_url, session, session_http: ClientSession = None): +async def go_forward(server_url, session, session_http: Union[ClientSession, None] = None): """Go to page forward""" try: url = f"{server_url}/session/{session}/forward" - payload = {} + payload: dict = {} await _post(url, payload, session_http=session_http) return True except Exception as e: @@ -140,7 +142,7 @@ async def set_window_rectangle(server_url, session, width, height, x, y, session raise WebDriverError("Failed to set window rectangle.") from e -async def fullscreen_window(server_url, session, session_http: ClientSession = None): +async def fullscreen_window(server_url, session, session_http: Union[ClientSession, None] = None): """Fullscreen window""" try: return await _handle_window( @@ -150,7 +152,7 @@ async def fullscreen_window(server_url, session, session_http: ClientSession = N raise WebDriverError("Failed to fullscreen window.") from e -async def minimize_window(server_url, session, session_http: ClientSession = None): +async def minimize_window(server_url, session, session_http: Union[ClientSession, None] = None): """Minimize window""" try: return await _handle_window( @@ -160,7 +162,7 @@ async def minimize_window(server_url, session, session_http: ClientSession = Non raise WebDriverError("Failed to minimize window.") from e -async def maximize_window(server_url, session, session_http: ClientSession = None): +async def maximize_window(server_url, session, session_http: Union[ClientSession, None] = None): """Maximize window""" try: return await _handle_window( @@ -170,7 +172,9 @@ async def maximize_window(server_url, session, session_http: ClientSession = Non raise WebDriverError("Failed to maximize window.") from e -async def switch_to_window(server_url, session, handle, session_http: ClientSession = None): +async def switch_to_window( + server_url, session, handle, session_http: Union[ClientSession, None] = None +): """Switch to window""" try: url = f"{server_url}/session/{session}/window" @@ -182,7 +186,7 @@ async def switch_to_window(server_url, session, handle, session_http: ClientSess async def new_window( - server_url, session, window_type="tab", session_http: ClientSession = None + server_url, session, window_type="tab", session_http: Union[ClientSession, None] = None ) -> str: """Open a new window :param window_type (str): tab or window @@ -199,7 +203,7 @@ async def new_window( async def switch_to_parent_frame( - server_url, session, element_frame, session_http: ClientSession = None + server_url, session, element_frame, session_http: Union[ClientSession, None] = None ): """Switch to parent frame of 'element_frame'""" try: @@ -211,7 +215,9 @@ async def switch_to_parent_frame( raise WebDriverError("Failed to switch to parent frame.") from e -async def switch_to_frame(server_url, session, element_frame, session_http: ClientSession = None): +async def switch_to_frame( + server_url, session, element_frame, session_http: Union[ClientSession, None] = None +): """Switch to frame 'element_frame'""" try: url = f"{server_url}/session/{session}/frame" @@ -222,7 +228,7 @@ async def switch_to_frame(server_url, session, element_frame, session_http: Clie raise WebDriverError("Failed to switch to frame.") from e -async def delete_all_cookies(server_url, session, session_http: ClientSession = None): +async def delete_all_cookies(server_url, session, session_http: Union[ClientSession, None] = None): """Delete all cookies""" try: url = f"{server_url}/session/{session}/cookie" @@ -232,7 +238,9 @@ async def delete_all_cookies(server_url, session, session_http: ClientSession = raise WebDriverError("Failed to delete cookies.") from e -async def send_alert_text(server_url, session, text, session_http: ClientSession = None): +async def send_alert_text( + server_url, session, text, session_http: Union[ClientSession, None] = None +): """Fill the alert text area and send the text""" try: url = f"{server_url}/session/{session}/alert/text" @@ -245,7 +253,7 @@ async def send_alert_text(server_url, session, text, session_http: ClientSession raise WebDriverError("Failed to sent text to alert.") from e -async def accept_alert(server_url, session, session_http: ClientSession = None): +async def accept_alert(server_url, session, session_http: Union[ClientSession, None] = None): """Accept alert""" try: return await _handle_alert(server_url, session, "accept", session_http=session_http) @@ -253,7 +261,7 @@ async def accept_alert(server_url, session, session_http: ClientSession = None): raise WebDriverError("Failed to accept alert.") from e -async def dismiss_alert(server_url, session, session_http: ClientSession = None): +async def dismiss_alert(server_url, session, session_http: Union[ClientSession, None] = None): """Dismiss alert""" try: return await _handle_alert(server_url, session, "dismiss", session_http=session_http) @@ -267,7 +275,7 @@ async def take_screenshot_element( element, path="/tmp", file_name="caqui", - session_http: ClientSession = None, + session_http: Union[ClientSession, None] = None, ): """Take screenshot of element""" try: @@ -285,7 +293,7 @@ async def take_screenshot( session, path="/tmp", file_name="caqui", - session_http: ClientSession = None, + session_http: Union[ClientSession, None] = None, ): """Take screenshot""" try: @@ -298,7 +306,9 @@ async def take_screenshot( raise WebDriverError("Failed to take screenshot.") from e -async def get_named_cookie(server_url, session, name, session_http: ClientSession = None) -> str: +async def get_named_cookie( + server_url, session, name, session_http: Union[ClientSession, None] = None +) -> str: """Get cookie by name""" try: url = f"{server_url}/session/{session}/cookie/{name}" @@ -309,7 +319,7 @@ async def get_named_cookie(server_url, session, name, session_http: ClientSessio async def get_computed_label( - server_url, session, element, session_http: ClientSession = None + server_url, session, element, session_http: Union[ClientSession, None] = None ) -> str: """Get the element tag computed label. Get the accessibility name""" try: @@ -321,7 +331,7 @@ async def get_computed_label( async def get_computed_role( - server_url, session, element, session_http: ClientSession = None + server_url, session, element, session_http: Union[ClientSession, None] = None ) -> str: """Get the element tag computed role (the element role)""" try: @@ -332,7 +342,9 @@ async def get_computed_role( raise WebDriverError("Failed to get element computed role.") from e -async def get_tag_name(server_url, session, element, session_http: ClientSession = None) -> str: +async def get_tag_name( + server_url, session, element, session_http: Union[ClientSession, None] = None +) -> str: """Get the element tag name""" try: url = f"{server_url}/session/{session}/element/{element}/name" @@ -342,7 +354,9 @@ async def get_tag_name(server_url, session, element, session_http: ClientSession raise WebDriverError("Failed to get element name.") from e -async def get_shadow_root(server_url, session, element, session_http: ClientSession = None) -> dict: +async def get_shadow_root( + server_url, session, element, session_http: Union[ClientSession, None] = None +) -> dict: """Get the shadow root element""" try: root_element = "shadow-6066-11e4-a52e-4f735466cecf" @@ -353,7 +367,9 @@ async def get_shadow_root(server_url, session, element, session_http: ClientSess raise WebDriverError("Failed to get element shadow.") from e -async def get_rect(server_url, session, element, session_http: ClientSession = None) -> dict: +async def get_rect( + server_url, session, element, session_http: Union[ClientSession, None] = None +) -> dict: """Get the element rectangle""" try: url = f"{server_url}/session/{session}/element/{element}/rect" @@ -363,13 +379,15 @@ async def get_rect(server_url, session, element, session_http: ClientSession = N raise WebDriverError("Failed to get element rect.") from e -async def actions(server_url, session, payload, session_http: ClientSession = None): +async def actions(server_url, session, payload, session_http: Union[ClientSession, None] = None): url = f"{server_url}/session/{session}/actions" await _post(url, payload, session_http=session_http) return True -async def actions_move_to_element(server_url, session, element, session_http: ClientSession = None): +async def actions_move_to_element( + server_url, session, element, session_http: Union[ClientSession, None] = None +): """Move to an element simulating a mouse movement""" try: payload = { @@ -401,7 +419,7 @@ async def actions_move_to_element(server_url, session, element, session_http: Cl async def actions_scroll_to_element( - server_url, session, element, session_http: ClientSession = None + server_url, session, element, session_http: Union[ClientSession, None] = None ): """Scroll to an element simulating a mouse movement""" try: @@ -429,7 +447,7 @@ async def actions_scroll_to_element( raise WebDriverError("Failed to scroll to element.") from e -async def submit(server_url, session, element, session_http: ClientSession = None): +async def submit(server_url, session, element, session_http: Union[ClientSession, None] = None): """Submit a form. It is similar to 'submit' funtion in Seleniu It is not part of W3C WebDriver. Just added for convenience """ @@ -447,7 +465,9 @@ async def submit(server_url, session, element, session_http: ClientSession = Non raise WebDriverError("Failed to submit form.") from e -async def actions_click(server_url, session, element, session_http: ClientSession = None): +async def actions_click( + server_url, session, element, session_http: Union[ClientSession, None] = None +): """Click an element simulating a mouse movement""" try: payload = { @@ -484,7 +504,9 @@ async def actions_click(server_url, session, element, session_http: ClientSessio raise WebDriverError("Failed to click the element.") from e -async def set_timeouts(server_url, session, timeouts, session_http: ClientSession = None): +async def set_timeouts( + server_url, session, timeouts, session_http: Union[ClientSession, None] = None +): """Set timeouts""" try: url = f"{server_url}/session/{session}/timeouts" @@ -503,7 +525,7 @@ async def find_children_elements( parent_element, locator_type, locator_value, - session_http: ClientSession = None, + session_http: Union[ClientSession, None] = None, ): """Find the children elements by 'locator_type' @@ -527,7 +549,7 @@ async def find_child_element( parent_element, locator_type, locator_value, - session_http: ClientSession = None, + session_http: Union[ClientSession, None] = None, ): """Find the child element by 'locator_type'""" try: @@ -539,7 +561,9 @@ async def find_child_element( raise WebDriverError(f"Failed to find the child element from '{parent_element}'.") from e -async def get_page_source(server_url, session, session_http: ClientSession = None) -> str: +async def get_page_source( + server_url, session, session_http: Union[ClientSession, None] = None +) -> str: """Get the page source (all content)""" try: url = f"{server_url}/session/{session}/source" @@ -549,7 +573,9 @@ async def get_page_source(server_url, session, session_http: ClientSession = Non raise WebDriverError("Failed to get the page source.") from e -async def execute_script(server_url, session, script, args=[], session_http: ClientSession = None): +async def execute_script( + server_url, session, script, args=[], session_http: Union[ClientSession, None] = None +): """Executes a script, like 'alert('something')' to open an alert window""" try: url = f"{server_url}/session/{session}/execute/sync" @@ -560,7 +586,9 @@ async def execute_script(server_url, session, script, args=[], session_http: Cli raise WebDriverError("Failed to execute script.") from e -async def get_alert_text(server_url, session, session_http: ClientSession = None) -> str: +async def get_alert_text( + server_url, session, session_http: Union[ClientSession, None] = None +) -> str: """Get the text from an alert""" try: url = f"{server_url}/session/{session}/alert/text" @@ -570,7 +598,7 @@ async def get_alert_text(server_url, session, session_http: ClientSession = None raise WebDriverError("Failed to get the alert text.") from e -async def get_active_element(server_url, session, session_http: ClientSession = None): +async def get_active_element(server_url, session, session_http: Union[ClientSession, None] = None): """Get the active element""" try: url = f"{server_url}/session/{session}/element/active" @@ -580,7 +608,9 @@ async def get_active_element(server_url, session, session_http: ClientSession = raise WebDriverError("Failed to check if element is selected.") from e -async def clear_element(server_url, session, element, session_http: ClientSession = None): +async def clear_element( + server_url, session, element, session_http: Union[ClientSession, None] = None +): """Clear the element text""" try: url = f"{server_url}/session/{session}/element/{element}/clear" @@ -592,7 +622,7 @@ async def clear_element(server_url, session, element, session_http: ClientSessio async def is_element_enabled( - server_url, session, element, session_http: ClientSession = None + server_url, session, element, session_http: Union[ClientSession, None] = None ) -> bool: """Check if element is enabled""" try: @@ -604,7 +634,7 @@ async def is_element_enabled( async def get_css_value( - server_url, session, element, property_name, session_http: ClientSession = None + server_url, session, element, property_name, session_http: Union[ClientSession, None] = None ) -> str: """Check if element is selected""" try: @@ -616,18 +646,20 @@ async def get_css_value( async def is_element_selected( - server_url, session, element, session_http: ClientSession = None -) -> str: + server_url, session, element, session_http: Union[ClientSession, None] = None +) -> bool: """Check if element is selected""" try: url = f"{server_url}/session/{session}/element/{element}/selected" response = await _get(url, session_http=session_http) - return response.get("value") + return bool(response.get("value")) except Exception as e: raise WebDriverError("Failed to check if element is selected.") from e -async def get_window_rectangle(server_url, session, session_http: ClientSession = None) -> dict: +async def get_window_rectangle( + server_url, session, session_http: Union[ClientSession, None] = None +) -> dict: """Get window rectangle""" try: url = f"{server_url}/session/{session}/window/rect" @@ -637,7 +669,9 @@ async def get_window_rectangle(server_url, session, session_http: ClientSession raise WebDriverError("Failed to get window rectangle.") from e -async def get_window_handles(server_url, session, session_http: ClientSession = None) -> list: +async def get_window_handles( + server_url, session, session_http: Union[ClientSession, None] = None +) -> list: """Get window handles""" try: url = f"{server_url}/session/{session}/window/handles" @@ -647,7 +681,9 @@ async def get_window_handles(server_url, session, session_http: ClientSession = raise WebDriverError("Failed to get window handles.") from e -async def close_window(server_url, session, session_http: ClientSession = None) -> list: +async def close_window( + server_url, session, session_http: Union[ClientSession, None] = None +) -> list: """Close active window""" try: url = f"{server_url}/session/{session}/window" @@ -657,7 +693,7 @@ async def close_window(server_url, session, session_http: ClientSession = None) raise WebDriverError("Failed to close active window.") from e -async def get_window(server_url, session, session_http: ClientSession = None) -> str: +async def get_window(server_url, session, session_http: Union[ClientSession, None] = None) -> str: """Get window""" try: url = f"{server_url}/session/{session}/window" @@ -667,7 +703,7 @@ async def get_window(server_url, session, session_http: ClientSession = None) -> raise WebDriverError("Failed to get window.") from e -async def go_back(server_url, session, session_http: ClientSession = None): +async def go_back(server_url, session, session_http: Union[ClientSession, None] = None): """ This command causes the browser to traverse one step backward in the joint session history of the @@ -681,7 +717,7 @@ async def go_back(server_url, session, session_http: ClientSession = None): raise WebDriverError("Failed to go back to page.") from e -async def get_url(server_url, session, session_http: ClientSession = None) -> str: +async def get_url(server_url, session, session_http: Union[ClientSession, None] = None) -> str: """Returns the URL from web page:""" try: url = f"{server_url}/session/{session}/url" @@ -691,7 +727,9 @@ async def get_url(server_url, session, session_http: ClientSession = None) -> st raise WebDriverError("Failed to get page url.") from e -async def get_timeouts(server_url, session, session_http: ClientSession = None) -> dict: +async def get_timeouts( + server_url, session, session_http: Union[ClientSession, None] = None +) -> dict: """ Returns the configured timeouts: {"implicit": 0, "pageLoad": 300000, "script": 30000} @@ -704,7 +742,7 @@ async def get_timeouts(server_url, session, session_http: ClientSession = None) raise WebDriverError("Failed to get timeouts.") from e -async def get_status(server_url, session_http: ClientSession = None) -> dict: +async def get_status(server_url, session_http: Union[ClientSession, None] = None) -> dict: """Returns the status and details of the WebDriver""" try: url = f"{server_url}/status" @@ -714,7 +752,7 @@ async def get_status(server_url, session_http: ClientSession = None) -> dict: raise WebDriverError("Failed to get status.") from e -async def get_title(server_url, session, session_http: ClientSession = None) -> str: +async def get_title(server_url, session, session_http: Union[ClientSession, None] = None) -> str: """Get the page title""" try: url = f"{server_url}/session/{session}/title" @@ -725,8 +763,12 @@ async def get_title(server_url, session, session_http: ClientSession = None) -> async def find_elements( - server_url, session, locator_type, locator_value, session_http: ClientSession = None -) -> str: + server_url, + session, + locator_type, + locator_value, + session_http: Union[ClientSession, None] = None, +) -> List[Any]: """Search the DOM elements by 'locator', for example, 'xpath'""" try: payload = {"using": locator_type, "value": locator_value} @@ -740,7 +782,7 @@ async def find_elements( async def get_property( - server_url, session, element, property, session_http: ClientSession = None + server_url, session, element, property, session_http: Union[ClientSession, None] = None ) -> str: """Get the given HTML property of an element, for example, 'href'""" try: @@ -752,7 +794,7 @@ async def get_property( async def get_attribute( - server_url, session, element, attribute, session_http: ClientSession = None + server_url, session, element, attribute, session_http: Union[ClientSession, None] = None ) -> str: """Get the given HTML attribute of an element, for example, 'aria-valuenow'""" try: @@ -763,7 +805,9 @@ async def get_attribute( raise WebDriverError("Failed to get value from element.") from e -async def get_text(server_url, session, element, session_http: ClientSession = None) -> str: +async def get_text( + server_url, session, element, session_http: Union[ClientSession, None] = None +) -> str: """Get the text of an element""" try: url = f"{server_url}/session/{session}/element/{element}/text" @@ -773,7 +817,7 @@ async def get_text(server_url, session, element, session_http: ClientSession = N raise WebDriverError("Failed to get text from element.") from e -async def get_cookies(server_url, session, session_http: ClientSession = None) -> list: +async def get_cookies(server_url, session, session_http: Union[ClientSession, None] = None) -> list: """Get the page cookies""" try: url = f"{server_url}/session/{session}/cookie" @@ -783,7 +827,7 @@ async def get_cookies(server_url, session, session_http: ClientSession = None) - raise WebDriverError("Failed to get page cookies.") from e -async def close_session(server_url, session, session_http: ClientSession = None): +async def close_session(server_url, session, session_http: Union[ClientSession, None] = None): """Close an opened session and close the browser""" try: url = f"{server_url}/session/{session}" @@ -793,12 +837,14 @@ async def close_session(server_url, session, session_http: ClientSession = None) raise WebDriverError("Failed to close session.") from e -async def get(server_url, session, page_url, session_http: ClientSession = None): +async def get(server_url, session, page_url, session_http: Union[ClientSession, None] = None): """Does the same of 'go_to_page'. Added to be compatible with selenium method name'""" return go_to_page(server_url, session, page_url, session_http=session_http) -async def go_to_page(server_url, session, page_url, session_http: ClientSession = None): +async def go_to_page( + server_url, session, page_url, session_http: Union[ClientSession, None] = None +): """Navigate to 'page_url'""" try: url = f"{server_url}/session/{session}/url" @@ -809,7 +855,9 @@ async def go_to_page(server_url, session, page_url, session_http: ClientSession raise WebDriverError(f"Failed to navigate to page '{page_url}'.") from e -async def send_keys(server_url, session, element, text, session_http: ClientSession = None): +async def send_keys( + server_url, session, element, text, session_http: Union[ClientSession, None] = None +): """Fill an editable element, for example a textarea, with a given text""" try: url = f"{server_url}/session/{session}/element/{element}/value" @@ -820,7 +868,7 @@ async def send_keys(server_url, session, element, text, session_http: ClientSess raise WebDriverError(f"Failed to send key '{text}'.") from e -async def click(server_url, session, element, session_http: ClientSession = None): +async def click(server_url, session, element, session_http: Union[ClientSession, None] = None): """Click on an element""" try: payload = {"id": element} @@ -832,8 +880,12 @@ async def click(server_url, session, element, session_http: ClientSession = None async def find_element( - server_url, session, locator_type, locator_value, session_http: ClientSession = None -) -> dict: + server_url, + session, + locator_type, + locator_value, + session_http: Union[ClientSession, None] = None, +) -> str: """Find an element by a 'locator', for example 'xpath'""" try: @@ -855,7 +907,7 @@ async def find_element( async def get_session( server_url: str, capabilities: Optional[dict] = None, - session_http: ClientSession = None, + session_http: Union[ClientSession, None] = None, ) -> str: """ Opens a browser and a session. diff --git a/caqui/easy/action_chains.py b/caqui/easy/action_chains.py index cc7181e..fc1002a 100644 --- a/caqui/easy/action_chains.py +++ b/caqui/easy/action_chains.py @@ -1,6 +1,6 @@ from caqui import asynchronous from caqui.easy.element import Element -from typing import Coroutine +from typing import Coroutine, Union, List class ActionChains: @@ -8,8 +8,8 @@ def __init__(self, driver) -> None: self._remote = driver.remote self._session = driver.session self._session_http = driver.session_http - self._coroutines: list[Coroutine] = [] - self._element = None + self._coroutines: List[Coroutine] = [] + self._element = Union[Element, None] def click(self, element: Element): """ @@ -31,7 +31,7 @@ def move_to_element(self, element: Element): self._coroutines.append(coroutine) return self - def scroll_to_element(self, element: Element): + def scroll_to_element(self, element: Element) -> "ActionChains": """Scrolls the screen to the element `element`""" self._element = element coroutine = asynchronous.actions_scroll_to_element( diff --git a/caqui/easy/capabilities.py b/caqui/easy/capabilities.py index a5c90e6..7429f20 100644 --- a/caqui/easy/capabilities.py +++ b/caqui/easy/capabilities.py @@ -228,7 +228,7 @@ def proxy(self, proxy_configuration: Union[dict, ProxyConfigurationBuilder]): proxy_configuration = proxy_configuration.to_dict() self.desired_capabilities = { **self.desired_capabilities, - **proxy_configuration, + **proxy_configuration, # type: ignore } return self @@ -251,7 +251,7 @@ def timeouts(self, session_timeouts: Union[dict, TimeoutsBuilder]): session_timeouts = session_timeouts.to_dict() self.desired_capabilities = { **self.desired_capabilities, - **session_timeouts, + **session_timeouts, # type: ignore } return self diff --git a/caqui/easy/element.py b/caqui/easy/element.py index fd96643..1fcbd3a 100644 --- a/caqui/easy/element.py +++ b/caqui/easy/element.py @@ -149,7 +149,7 @@ async def click(self): self._remote, self._session, self._element, session_http=self._session_http ) - async def find_elements(self, locator, value): + async def find_elements(self, locator, value) -> list: """ Find the children elements by 'locator_type' @@ -169,7 +169,7 @@ async def find_elements(self, locator, value): result.append(Element(element, self._driver)) return result - async def find_element(self, locator, value): + async def find_element(self, locator, value) -> "Element": """Find the element by `locator_type`""" element = await asynchronous.find_child_element( self._remote, diff --git a/caqui/easy/options.py b/caqui/easy/options.py index 71cb008..b9e68b3 100644 --- a/caqui/easy/options.py +++ b/caqui/easy/options.py @@ -93,7 +93,7 @@ def windows_types(self, values: list): self.options = {**self.options, **{"windowsTypes": values}} return self - def to_dict(self): + def to_dict(self) -> dict: """Converts the options to a dict""" return {"goog:chromeOptions": self.options} diff --git a/caqui/easy/page.py b/caqui/easy/page.py index 8bbccbf..5c2a6f3 100644 --- a/caqui/easy/page.py +++ b/caqui/easy/page.py @@ -17,7 +17,7 @@ def __init__( server_url: str, capabilities: Optional[dict] = None, url: Union[str, None] = None, - session_http: ClientSession = None, + session_http: Union[ClientSession, None] = None, ) -> None: """Mimics Selenium methods""" self.session_http = session_http @@ -231,7 +231,7 @@ async def get(self, url): self._server_url, self._session, url, session_http=self.session_http ) - async def find_elements(self, locator, value): + async def find_elements(self, locator, value) -> list: """Search the DOM elements by 'locator', for example, 'xpath'""" elements = await asynchronous.find_elements( self._server_url, self._session, locator, value, session_http=self.session_http @@ -241,7 +241,7 @@ async def find_elements(self, locator, value): result.append(Element(element, self)) return result - async def find_element(self, locator, value): + async def find_element(self, locator, value) -> Element: """Find an element by a 'locator', for example 'xpath'""" element = await asynchronous.find_element( self._server_url, self._session, locator, value, session_http=self.session_http diff --git a/caqui/synchronous.py b/caqui/synchronous.py index 5293db8..315c39b 100644 --- a/caqui/synchronous.py +++ b/caqui/synchronous.py @@ -752,7 +752,7 @@ def get_session(server_url: str, capabilities: Optional[dict] = None): raise WebDriverError("Failed to open session. Check the browser capabilities.") from e -def find_element(server_url, session, locator_type, locator_value) -> dict: +def find_element(server_url, session, locator_type, locator_value) -> str: """Find an element by a 'locator', for example 'xpath'""" try: url = f"{server_url}/session/{session}/element" diff --git a/data-processed.txt b/data-processed.txt index 6a0b15d..4a52274 100644 --- a/data-processed.txt +++ b/data-processed.txt @@ -234,3 +234,16 @@ OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup' OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) ===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== diff --git a/utils/generate-pyx-files.py b/utils/build-pyx-files.py similarity index 100% rename from utils/generate-pyx-files.py rename to utils/build-pyx-files.py diff --git a/utils/cleanup-cpython-files.py b/utils/cleanup-cpython-files.py new file mode 100644 index 0000000..f528cfe --- /dev/null +++ b/utils/cleanup-cpython-files.py @@ -0,0 +1,32 @@ +import os + + +def remove_cython_build_files(root_dir): + """ + Removes .pyx and .c files from the specified root directory and its subdirectories. + + Args: + root_dir (str): The path to the root directory to clean. + """ + for dirpath, dirnames, filenames in os.walk(root_dir): + for filename in filenames: + if filename.endswith(".pyx") or filename.endswith(".c") or filename.endswith(".so"): + file_path = os.path.join(dirpath, filename) + try: + os.remove(file_path) + print(f"Removed: {file_path}") + except OSError as e: + print(f"Error removing {file_path}: {e}") + + +if __name__ == "__main__": + # Specify the directory to clean. + # For example, to clean the current directory: + # directory_to_clean = "." + # Or a specific path: + directory_to_clean = "./caqui" + + if os.path.exists(directory_to_clean): + remove_cython_build_files(directory_to_clean) + else: + print(f"Error: Directory '{directory_to_clean}' not found.") From 51bf4250f5eaeb96aa8c0b3ce6bacd960a441717 Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Sun, 30 Nov 2025 11:51:01 -0300 Subject: [PATCH 08/40] build using cython working --- caqui/asynchronous.py | 50 +++++----- caqui/easy/page.py | 2 +- caqui/easy/server.py | 4 +- caqui/easy/switch_to.py | 5 +- caqui/easy/window.py | 6 +- caqui/synchronous.py | 22 +++-- data-processed.txt | 91 +++++++++++++++++++ pyproject.toml | 16 +++- pytest.ini | 2 +- setup.py | 26 +----- tests/conftest.py | 14 +-- tests/feature/test_async_with_http_session.py | 41 ++++----- tests/feature/test_sync_and_async.py | 37 ++++---- tests/integration/test_capabilities.py | 4 +- tests/integration/test_options.py | 4 +- tests/performance/README.md | 5 +- tests/unit/test_async_unit.py | 6 +- tests/unit/test_by.py | 3 +- tests/unit/test_sync_unit.py | 6 +- 19 files changed, 219 insertions(+), 125 deletions(-) diff --git a/caqui/asynchronous.py b/caqui/asynchronous.py index 42b20be..e4a8315 100644 --- a/caqui/asynchronous.py +++ b/caqui/asynchronous.py @@ -6,7 +6,7 @@ from typing import Any, List, Optional, Union -async def _handle_response(resp): +async def _handle_response(resp) -> Any: result = None if resp.status in range(200, 399): result = await resp.json() @@ -54,7 +54,7 @@ async def _post(url, payload, session_http: Union[ClientSession, None] = None): raise WebDriverError("'POST' request failed.") from e -async def _get(url, session_http: Union[ClientSession, None] = None): +async def _get(url, session_http: Union[ClientSession, None] = None) -> dict: if session_http: try: async with session_http.get(url, headers=HEADERS) as resp: @@ -308,12 +308,12 @@ async def take_screenshot( async def get_named_cookie( server_url, session, name, session_http: Union[ClientSession, None] = None -) -> str: +) -> dict: """Get cookie by name""" try: url = f"{server_url}/session/{session}/cookie/{name}" response = await _get(url, session_http) - return response.get("value") + return response.get("value", {}) except Exception as e: raise WebDriverError(f"Failed to get cookie '{name}'.") from e @@ -325,7 +325,7 @@ async def get_computed_label( try: url = f"{server_url}/session/{session}/element/{element}/computedlabel" response = await _get(url, session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get element computed label.") from e @@ -337,7 +337,7 @@ async def get_computed_role( try: url = f"{server_url}/session/{session}/element/{element}/computedrole" response = await _get(url, session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get element computed role.") from e @@ -349,14 +349,14 @@ async def get_tag_name( try: url = f"{server_url}/session/{session}/element/{element}/name" response = await _get(url, session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get element name.") from e async def get_shadow_root( server_url, session, element, session_http: Union[ClientSession, None] = None -) -> dict: +) -> str: """Get the shadow root element""" try: root_element = "shadow-6066-11e4-a52e-4f735466cecf" @@ -374,7 +374,7 @@ async def get_rect( try: url = f"{server_url}/session/{session}/element/{element}/rect" response = await _get(url, session_http) - return response.get("value") + return response.get("value", {}) except Exception as e: raise WebDriverError("Failed to get element rect.") from e @@ -568,7 +568,7 @@ async def get_page_source( try: url = f"{server_url}/session/{session}/source" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get the page source.") from e @@ -593,7 +593,7 @@ async def get_alert_text( try: url = f"{server_url}/session/{session}/alert/text" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get the alert text.") from e @@ -628,7 +628,7 @@ async def is_element_enabled( try: url = f"{server_url}/session/{session}/element/{element}/enabled" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", False) except Exception as e: raise WebDriverError("Failed to check if element is enabled.") from e @@ -636,13 +636,13 @@ async def is_element_enabled( async def get_css_value( server_url, session, element, property_name, session_http: Union[ClientSession, None] = None ) -> str: - """Check if element is selected""" + """Get CSS value""" try: url = f"{server_url}/session/{session}/element/{element}/css/{property_name}" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: - raise WebDriverError("Failed to check if element is selected.") from e + raise WebDriverError("Failed to get css value.") from e async def is_element_selected( @@ -664,7 +664,7 @@ async def get_window_rectangle( try: url = f"{server_url}/session/{session}/window/rect" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", {}) except Exception as e: raise WebDriverError("Failed to get window rectangle.") from e @@ -676,7 +676,7 @@ async def get_window_handles( try: url = f"{server_url}/session/{session}/window/handles" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", []) except Exception as e: raise WebDriverError("Failed to get window handles.") from e @@ -698,7 +698,7 @@ async def get_window(server_url, session, session_http: Union[ClientSession, Non try: url = f"{server_url}/session/{session}/window" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get window.") from e @@ -722,7 +722,7 @@ async def get_url(server_url, session, session_http: Union[ClientSession, None] try: url = f"{server_url}/session/{session}/url" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get page url.") from e @@ -737,7 +737,7 @@ async def get_timeouts( try: url = f"{server_url}/session/{session}/timeouts" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", {}) except Exception as e: raise WebDriverError("Failed to get timeouts.") from e @@ -757,7 +757,7 @@ async def get_title(server_url, session, session_http: Union[ClientSession, None try: url = f"{server_url}/session/{session}/title" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get page title.") from e @@ -783,7 +783,7 @@ async def find_elements( async def get_property( server_url, session, element, property, session_http: Union[ClientSession, None] = None -) -> str: +) -> Any: """Get the given HTML property of an element, for example, 'href'""" try: url = f"{server_url}/session/{session}/element/{element}/property/{property}" @@ -800,7 +800,7 @@ async def get_attribute( try: url = f"{server_url}/session/{session}/element/{element}/attribute/{attribute}" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get value from element.") from e @@ -812,7 +812,7 @@ async def get_text( try: url = f"{server_url}/session/{session}/element/{element}/text" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", "") except Exception as e: raise WebDriverError("Failed to get text from element.") from e @@ -822,7 +822,7 @@ async def get_cookies(server_url, session, session_http: Union[ClientSession, No try: url = f"{server_url}/session/{session}/cookie" response = await _get(url, session_http=session_http) - return response.get("value") + return response.get("value", []) except Exception as e: raise WebDriverError("Failed to get page cookies.") from e diff --git a/caqui/easy/page.py b/caqui/easy/page.py index 5c2a6f3..5801861 100644 --- a/caqui/easy/page.py +++ b/caqui/easy/page.py @@ -169,7 +169,7 @@ async def get_cookies(self): self._server_url, self._session, session_http=self.session_http ) - async def get_cookie(self, cookie_name): + async def get_cookie(self, cookie_name) -> dict: """Get the desired cookie""" return await asynchronous.get_named_cookie( self._server_url, self._session, cookie_name, session_http=self.session_http diff --git a/caqui/easy/server.py b/caqui/easy/server.py index 89c1d00..3546e5f 100644 --- a/caqui/easy/server.py +++ b/caqui/easy/server.py @@ -45,7 +45,7 @@ def _wait_server(self): break except ConnectionError: sleep(0.5) - if i == (MAX_RETIES - 1): + if i == (MAX_RETIES - 1) and self._process: self._process.kill() self._process.wait() raise Exception("Driver not started") @@ -67,7 +67,7 @@ def start(self): raise driver_manager = self._browser_factory() - self._process = subprocess.Popen( + self._process : Union[subprocess.Popen, None] = subprocess.Popen( [driver_manager, f"--port={self._port}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, diff --git a/caqui/easy/switch_to.py b/caqui/easy/switch_to.py index 4c068d3..d7f8546 100644 --- a/caqui/easy/switch_to.py +++ b/caqui/easy/switch_to.py @@ -1,3 +1,4 @@ +from typing import Union from caqui import asynchronous, synchronous from caqui.easy.element import Element from caqui.easy.alert import Alert @@ -6,8 +7,8 @@ class SwitchTo: def __init__(self, driver) -> None: self._driver = driver - self._iframe = None - self._window_handle = None + self._iframe :Union[str, None]= None + self._window_handle:Union[str, None] = None self._session_http = driver.session_http @property diff --git a/caqui/easy/window.py b/caqui/easy/window.py index c7a67e2..d8746ab 100644 --- a/caqui/easy/window.py +++ b/caqui/easy/window.py @@ -3,8 +3,8 @@ class Window: def __init__(self, driver) -> None: - self.__remote = driver.remote - self.__session = driver.session + self._remote = driver.remote + self._ssession = driver.session async def new(self, window_type="tab"): """ @@ -14,4 +14,4 @@ async def new(self, window_type="tab"): return (str): window handle """ - return await asynchronous.new_window(self.__remote, self.__session, window_type) + return await asynchronous.new_window(self._remote, self._session, window_type) diff --git a/caqui/synchronous.py b/caqui/synchronous.py index 315c39b..4c3af06 100644 --- a/caqui/synchronous.py +++ b/caqui/synchronous.py @@ -3,7 +3,7 @@ from caqui.exceptions import WebDriverError from caqui import helper from caqui.constants import HEADERS -from typing import Optional +from typing import Any, Optional def _handle_response(response): @@ -53,7 +53,7 @@ def _handle_alerts(server_url, session, command): def _handle_window(server_url, session, command): url = f"{server_url}/session/{session}/window/{command}" - payload = {} + payload:dict = {} _post(url, payload) return True @@ -83,7 +83,7 @@ def refresh_page(server_url, session): """Refresh page""" try: url = f"{server_url}/session/{session}/refresh" - payload = {} + payload: dict = {} _post(url, payload) return True except Exception as e: @@ -94,7 +94,7 @@ def go_forward(server_url, session): """Go to page forward""" try: url = f"{server_url}/session/{session}/forward" - payload = {} + payload: dict = {} _post(url, payload) return True except Exception as e: @@ -242,7 +242,7 @@ def take_screenshot(server_url, session, path="/tmp", file_name="caqui"): raise WebDriverError("Failed to take screeshot.") from e -def get_named_cookie(server_url, session, name) -> str: +def get_named_cookie(server_url, session, name) -> dict: """Get cookie by name.""" try: url = f"{server_url}/session/{session}/cookie/{name}" @@ -278,7 +278,7 @@ def get_tag_name(server_url, session, element) -> str: raise WebDriverError("Failed to get the element name.") from e -def get_shadow_root(server_url, session, element) -> dict: +def get_shadow_root(server_url, session, element) -> str: """Get the shadow root element""" try: root_element = "shadow-6066-11e4-a52e-4f735466cecf" @@ -638,7 +638,7 @@ def find_elements(server_url, session, locator_type, locator_value) -> list: ) from e -def get_property(server_url, session, element, property_name) -> str: +def get_property(server_url, session, element, property_name) -> Any: """Get the given HTML property of an element, for example, 'href'""" try: url = f"{server_url}/session/{session}/element/{element}/property/{property_name}" @@ -751,9 +751,17 @@ def get_session(server_url: str, capabilities: Optional[dict] = None): except Exception as e: raise WebDriverError("Failed to open session. Check the browser capabilities.") from e +# from cssify import cssify def find_element(server_url, session, locator_type, locator_value) -> str: """Find an element by a 'locator', for example 'xpath'""" + # try: + # if locator_type.lower() == "xpath": + # locator_type = "css selector" + # locator_value = cssify(locator_value) + # except Exception: + # # just ignore it and keep using the xpath selector + # pass try: url = f"{server_url}/session/{session}/element" payload = {"using": locator_type, "value": locator_value} diff --git a/data-processed.txt b/data-processed.txt index 4a52274..642f5ce 100644 --- a/data-processed.txt +++ b/data-processed.txt @@ -247,3 +247,94 @@ OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup' OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) ===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== diff --git a/pyproject.toml b/pyproject.toml index d99ffef..1361491 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,15 +13,17 @@ authors = [ { name="Douglas Cardoso", email="noemail@noemail.com" }, ] description = "Run asynchronous commands in WebDrivers" +keywords = ["automation", "asynchronous", "test", "crawler", "robotic process automation", "rpa"] readme = "README.md" requires-python = ">=3.7" classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] +license="MIT" + dependencies = [ "requests", "aiohttp", @@ -33,3 +35,15 @@ dependencies = [ [project.urls] "Homepage" = "https://github.com/douglasdcm/caqui" "Bug Tracker" = "https://github.com/douglasdcm/caqui/issues" + +[tool.mypy] +exclude=["tests/performance/test_process_data.py", "tests/test_sniffer.py"] +disallow_any_unimported = false +disallow_any_expr = false +disallow_any_decorated = false +disallow_any_explicit = false +disallow_any_generics = false +disallow_subclassing_any = false +disallow_untyped_calls = false +disallow_untyped_defs=false +check_untyped_defs = true \ No newline at end of file diff --git a/pytest.ini b/pytest.ini index def8ab1..1a992ef 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,4 +3,4 @@ log_format = %(asctime)s.%(msecs)03d %(levelname)s %(message)s log_date_format = %Y-%m-%d %H:%M:%S log_cli = true log_cli_level = WARNING -asyncio_default_fixture_loop_scope=function \ No newline at end of file +; asyncio_d efault_fixture_loop_scope=function \ No newline at end of file diff --git a/setup.py b/setup.py index eee1001..55c9a85 100644 --- a/setup.py +++ b/setup.py @@ -1,26 +1,8 @@ -from distutils.core import setup -from Cython.Build import cythonize +from distutils.core import setup +from Cython.Build import cythonize setup( - ext_modules=cythonize( - [ - "./caqui/asynchronous.pyx", - "./caqui/constants.pyx", - "./caqui/helper.pyx", - "./caqui/exceptions.pyx", - "./caqui/synchronous.pyx", - "./caqui/by.pyx", - "./caqui/__init__.pyx", - "./caqui/easy/capabilities.pyx", - "./caqui/easy/options.pyx", - "./caqui/easy/action_chains.pyx", - "./caqui/easy/alert.pyx", - "./caqui/easy/page.pyx", - "./caqui/easy/switch_to.pyx", - "./caqui/easy/window.pyx", - "./caqui/easy/element.pyx", - "./caqui/easy/__init__.pyx", - "./caqui/easy/server.pyx", - ] + ext_modules = cythonize( + ['./caqui/asynchronous.pyx', './caqui/constants.pyx', './caqui/helper.pyx', './caqui/exceptions.pyx', './caqui/synchronous.pyx', './caqui/by.pyx', './caqui/__init__.pyx', './caqui/easy/capabilities.pyx', './caqui/easy/options.pyx', './caqui/easy/action_chains.pyx', './caqui/easy/alert.pyx', './caqui/easy/page.pyx', './caqui/easy/switch_to.pyx', './caqui/easy/window.pyx', './caqui/easy/element.pyx', './caqui/easy/__init__.pyx', './caqui/easy/server.pyx'] ) ) diff --git a/tests/conftest.py b/tests/conftest.py index 4a1d562..30110e2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,13 @@ CAPTURES = "captures" +@fixture(autouse=True, scope="session") +def setup_server(): + server = Server.get_instance(port=SERVER_PORT) + server.start() + # yield + # server.dispose(delay=3) + def _build_capabilities(): options = ChromeOptionsBuilder().args(["headless"]) capabilities = ( @@ -24,13 +31,6 @@ def _build_capabilities(): return capabilities -@fixture(autouse=True, scope="session") -def setup_server(): - server = Server.get_instance(port=SERVER_PORT) - server.start() - # yield - # server.dispose(delay=3) - @fixture def setup_functional_environment(): diff --git a/tests/feature/test_async_with_http_session.py b/tests/feature/test_async_with_http_session.py index 60e150e..1d0c8e7 100644 --- a/tests/feature/test_async_with_http_session.py +++ b/tests/feature/test_async_with_http_session.py @@ -1,3 +1,4 @@ +from typing import Union from aiohttp import ClientSession from pytest import mark, raises from caqui import asynchronous, synchronous @@ -119,8 +120,8 @@ async def test_set_window_rectangle(setup_functional_environment): width = 500 height = 300 window_rectangle_before = synchronous.get_window_rectangle(server_url, session) - x = window_rectangle_before.get("x") + 1 - y = window_rectangle_before.get("y") + 1 + x = window_rectangle_before.get("x", 0) + 1 + y = window_rectangle_before.get("y", 0) + 1 assert synchronous.set_window_rectangle(server_url, session, width, height, x, y) is True @@ -141,7 +142,6 @@ async def test_set_window_rectangle(setup_functional_environment): is True ) - window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before assert window_rectangle_after.get("height") != window_rectangle_before.get("height") @@ -160,8 +160,8 @@ async def test_fullscreen_window(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) synchronous.maximize_window(server_url, session) @@ -171,11 +171,10 @@ async def test_fullscreen_window(setup_functional_environment): is True ) - window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) @mark.skip(reason="does not work in headless mode") @@ -186,10 +185,10 @@ async def test_minimize_window(setup_functional_environment): assert synchronous.minimize_window(server_url, session) is True - window_rectangle_after = synchronous.get_window_rectangle(server_url, session) + window_rectangle_after :dict= synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") < window_rectangle_before.get("height") - assert window_rectangle_after.get("width") < window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) < window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) < window_rectangle_before.get("width", 0) synchronous.maximize_window(server_url, session) @@ -199,11 +198,10 @@ async def test_minimize_window(setup_functional_environment): is True ) - window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") < window_rectangle_before.get("height") - assert window_rectangle_after.get("width") < window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) < window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) < window_rectangle_before.get("width", 0) @mark.skip(reason="does not work in headless mode") @@ -220,8 +218,8 @@ async def test_maximize_window_asynchronous(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) @mark.skip(reason="does not work in headless mode") @@ -234,8 +232,8 @@ def test_maximize_window_synchronous(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) @mark.parametrize("window_type", ("tab", "window")) @@ -461,7 +459,7 @@ async def test_get_named_cookie(setup_functional_environment): response = await asynchronous.get_named_cookie( server_url, session, name, session_http=session_http ) - assert response.get("value") == expected + assert response == expected @mark.asyncio @@ -572,7 +570,6 @@ async def test_get_shadow_root(setup_functional_environment): server_url, session = setup_functional_environment locator_type = By.ID locator_value = "shadow-root" - element = synchronous.find_element(server_url, session, locator_type, locator_value) assert synchronous.get_shadow_root(server_url, session, element) is not None @@ -1052,10 +1049,10 @@ async def test_get_timeouts(setup_functional_environment): async def test_get_status(setup_functional_environment): server_url, _ = setup_functional_environment expected = "ready" - assert expected in synchronous.get_status(server_url).get("value") + assert expected in synchronous.get_status(server_url).get("value", []) async with ClientSession() as session_http: response = await asynchronous.get_status(server_url, session_http=session_http) - assert expected in response.get("value") + assert expected in response.get("value", []) @mark.asyncio diff --git a/tests/feature/test_sync_and_async.py b/tests/feature/test_sync_and_async.py index 389c402..b5b7e74 100644 --- a/tests/feature/test_sync_and_async.py +++ b/tests/feature/test_sync_and_async.py @@ -105,8 +105,8 @@ async def test_set_window_rectangle(setup_functional_environment): width = 500 height = 300 window_rectangle_before = synchronous.get_window_rectangle(server_url, session) - x = window_rectangle_before.get("x") + 1 - y = window_rectangle_before.get("y") + 1 + x = window_rectangle_before.get("x", 0) + 1 + y = window_rectangle_before.get("y", 0) + 1 assert synchronous.set_window_rectangle(server_url, session, width, height, x, y) is True @@ -121,7 +121,6 @@ async def test_set_window_rectangle(setup_functional_environment): assert await asynchronous.set_window_rectangle(server_url, session, width, height, x, y) is True - window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before assert window_rectangle_after.get("height") != window_rectangle_before.get("height") @@ -140,18 +139,17 @@ async def test_fullscreen_window(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) synchronous.maximize_window(server_url, session) assert await asynchronous.fullscreen_window(server_url, session) is True - window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height",0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) @mark.skip(reason="does not work in headless mode") @@ -164,18 +162,17 @@ async def test_minimize_window(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") < window_rectangle_before.get("height") - assert window_rectangle_after.get("width") < window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) < window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) < window_rectangle_before.get("width",0) synchronous.maximize_window(server_url, session) assert await asynchronous.minimize_window(server_url, session) is True - window_rectangle_after = None window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") < window_rectangle_before.get("height") - assert window_rectangle_after.get("width") < window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) < window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) < window_rectangle_before.get("width", 0) @mark.skip(reason="does not work in headless mode") @@ -188,8 +185,8 @@ async def test_maximize_window_asynchronous(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) @mark.skip(reason="does not work in headless mode") @@ -202,8 +199,8 @@ def test_maximize_window_synchronous(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height") > window_rectangle_before.get("height") - assert window_rectangle_after.get("width") > window_rectangle_before.get("width") + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) + assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) @mark.parametrize("window_type", ("tab", "window")) @@ -383,7 +380,7 @@ async def test_get_named_cookie(setup_functional_environment): assert synchronous.get_named_cookie(server_url, session, name).get("value") == expected response = await asynchronous.get_named_cookie(server_url, session, name) - assert response.get("value") == expected + assert response == expected @mark.asyncio @@ -848,9 +845,9 @@ async def test_get_timeouts(setup_functional_environment): async def test_get_status(setup_functional_environment): server_url, _ = setup_functional_environment expected = "ready" - assert expected in synchronous.get_status(server_url).get("value") + assert expected in synchronous.get_status(server_url).get("value", []) response = await asynchronous.get_status(server_url) - assert expected in response.get("value") + assert expected in response.get("value", []) @mark.asyncio diff --git a/tests/integration/test_capabilities.py b/tests/integration/test_capabilities.py index c0496b2..b0e4d01 100644 --- a/tests/integration/test_capabilities.py +++ b/tests/integration/test_capabilities.py @@ -61,7 +61,7 @@ def test_chrome_capabilities_with_options(): "excludeSwitches": ["sw1", "sw2"], "minidumpPath": "any", "mobileEmulation": {"any": "any"}, - "windowsTypes": "any", + "windowsTypes": ["any"], "perfLoggingPrefs": { "enableNetwork": False, "enablePage": False, @@ -84,7 +84,7 @@ def test_chrome_capabilities_with_options(): .exclude_switches(["sw1", "sw2"]) .minidump_path("any") .mobile_emulation({"any": "any"}) - .windows_types("any") + .windows_types(["any"]) .perf_logging_prefs( { "enableNetwork": False, diff --git a/tests/integration/test_options.py b/tests/integration/test_options.py index cc6c92c..cebc39a 100644 --- a/tests/integration/test_options.py +++ b/tests/integration/test_options.py @@ -64,7 +64,7 @@ def test_chrome_options(): "enablePage": False, "traceCategories": "devtools.network", }, - "windowsTypes": "any", + "windowsTypes": ["any"], } } options = options = ( @@ -79,7 +79,7 @@ def test_chrome_options(): .exclude_switches(["sw1", "sw2"]) .minidump_path("any") .mobile_emulation({"any": "any"}) - .windows_types("any") + .windows_types(["any"]) .perf_logging_prefs( { "enableNetwork": False, diff --git a/tests/performance/README.md b/tests/performance/README.md index 9698c50..7b25236 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -857,7 +857,10 @@ Note: This was the choosen library as it has more starts in github, recent commi Not planned. Need many refactoring and has gain for specific scenarios ## Scenario 8 - convert code to CPython +No sensitive improvement + +## Scenario 9 - convert `XPATH` to `CSS Selector` automatically - Execution 1 (duration): 27.57s -- Execution 3 (duration): 27.90s - Execution 2 (duration): 28.11s +- Execution 3 (duration): 27.90s - mean: 27.86s diff --git a/tests/unit/test_async_unit.py b/tests/unit/test_async_unit.py index 43e52f6..4c3c7d8 100644 --- a/tests/unit/test_async_unit.py +++ b/tests/unit/test_async_unit.py @@ -186,7 +186,7 @@ async def mock_request(*args, **kwargs): @mark.asyncio async def test_close_window(): - expected = [] + expected:list = [] async def mock_request(*args, **kwargs): return fake_responses.CLOSE_WINDOW @@ -265,7 +265,7 @@ async def mock_request(*args, **kwargs): with patch("caqui.asynchronous._get", mock_request): response = await asynchronous.get_status("") - assert response.get("value").get("ready") is True + assert response.get("value", {}).get("ready", False) is True @mark.asyncio @@ -281,7 +281,7 @@ async def mock_request(*args, **kwargs): @mark.asyncio async def test_get_cookies(): - expected = [] + expected:list = [] async def mock_request(*args, **kwargs): return fake_responses.GET_COOKIES diff --git a/tests/unit/test_by.py b/tests/unit/test_by.py index a48091a..1232c0d 100644 --- a/tests/unit/test_by.py +++ b/tests/unit/test_by.py @@ -17,4 +17,5 @@ ], ) def test_locators(setup_functional_environment, locator, value): - assert synchronous.find_element(*setup_functional_environment, locator, value) is not None + server_url, session = setup_functional_environment + assert synchronous.find_element(server_url, session, locator, value) is not None diff --git a/tests/unit/test_sync_unit.py b/tests/unit/test_sync_unit.py index 74f83e3..3e35eeb 100644 --- a/tests/unit/test_sync_unit.py +++ b/tests/unit/test_sync_unit.py @@ -108,7 +108,7 @@ def test_get_window_handles(*args): @patch("caqui.synchronous.request", return_value=fake_responses.CLOSE_WINDOW) def test_close_window(*args): - expected = [] + expected:list = [] assert synchronous.close_window("", "") == expected @@ -136,7 +136,7 @@ def test_get_timeouts(*args): @patch("caqui.synchronous.request", return_value=fake_responses.GET_STATUS) def test_get_status(*args): - assert synchronous.get_status("").get("value").get("ready") is True + assert synchronous.get_status("").get("value", {}).get("ready", False) is True @patch("caqui.synchronous.request", return_value=fake_responses.GET_TITLE) @@ -148,7 +148,7 @@ def test_get_title(*args): @patch("caqui.synchronous.request", return_value=fake_responses.GET_COOKIES) def test_get_cookies(*args): - expected = [] + expected :list= [] assert synchronous.get_cookies("", "") == expected From fb447394a384728f89097d04021f5ccb46656391 Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Sun, 30 Nov 2025 12:51:20 -0300 Subject: [PATCH 09/40] add cssify --- caqui/asynchronous.py | 43 ++++++++------- caqui/easy/server.py | 2 +- caqui/easy/switch_to.py | 4 +- caqui/easy/window.py | 2 +- caqui/helper.py | 12 +++++ caqui/synchronous.py | 40 +++++++------- data-processed.txt | 52 +++++++++++++++++++ dev-requirements.txt | 3 +- pyproject.toml | 6 ++- setup.py | 26 ++++++++-- tests/conftest.py | 2 +- tests/feature/test_async_with_http_session.py | 2 +- tests/feature/test_sync_and_async.py | 4 +- tests/unit/test_async_unit.py | 4 +- tests/unit/test_sync_unit.py | 4 +- utils/run-mypy.sh | 1 + 16 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 utils/run-mypy.sh diff --git a/caqui/asynchronous.py b/caqui/asynchronous.py index e4a8315..b237d53 100644 --- a/caqui/asynchronous.py +++ b/caqui/asynchronous.py @@ -2,7 +2,7 @@ from orjson import dumps from caqui.constants import HEADERS from caqui.exceptions import WebDriverError -from caqui.helper import save_picture, get_elements, get_element +from caqui.helper import convert_xpath_to_css_selector, save_picture, get_elements, get_element from typing import Any, List, Optional, Union @@ -520,11 +520,11 @@ async def set_timeouts( async def find_children_elements( - server_url, - session, - parent_element, - locator_type, - locator_value, + server_url: str, + session: str, + parent_element: str, + locator_type: str, + locator_value: str, session_http: Union[ClientSession, None] = None, ): """Find the children elements by 'locator_type' @@ -532,6 +532,7 @@ async def find_children_elements( If the 'parent_element' is a shadow element, set the 'locator_type' as 'id' or 'css selector' """ + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: url = f"{server_url}/session/{session}/element/{parent_element}/elements" payload = {"using": locator_type, "value": locator_value, "id": parent_element} @@ -544,14 +545,15 @@ async def find_children_elements( async def find_child_element( - server_url, - session, - parent_element, - locator_type, - locator_value, + server_url: str, + session: str, + parent_element: str, + locator_type: str, + locator_value: str, session_http: Union[ClientSession, None] = None, ): """Find the child element by 'locator_type'""" + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: url = f"{server_url}/session/{session}/element/{parent_element}/element" payload = {"using": locator_type, "value": locator_value, "id": parent_element} @@ -763,13 +765,14 @@ async def get_title(server_url, session, session_http: Union[ClientSession, None async def find_elements( - server_url, - session, - locator_type, - locator_value, + server_url: str, + session: str, + locator_type: str, + locator_value: str, session_http: Union[ClientSession, None] = None, ) -> List[Any]: """Search the DOM elements by 'locator', for example, 'xpath'""" + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: payload = {"using": locator_type, "value": locator_value} url = f"{server_url}/session/{session}/elements" @@ -880,14 +883,14 @@ async def click(server_url, session, element, session_http: Union[ClientSession, async def find_element( - server_url, - session, - locator_type, - locator_value, + server_url: str, + session: str, + locator_type: str, + locator_value: str, session_http: Union[ClientSession, None] = None, ) -> str: """Find an element by a 'locator', for example 'xpath'""" - + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: payload = {"using": locator_type, "value": locator_value} url = f"{server_url}/session/{session}/element" diff --git a/caqui/easy/server.py b/caqui/easy/server.py index 3546e5f..c5575b3 100644 --- a/caqui/easy/server.py +++ b/caqui/easy/server.py @@ -67,7 +67,7 @@ def start(self): raise driver_manager = self._browser_factory() - self._process : Union[subprocess.Popen, None] = subprocess.Popen( + self._process: Union[subprocess.Popen, None] = subprocess.Popen( [driver_manager, f"--port={self._port}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, diff --git a/caqui/easy/switch_to.py b/caqui/easy/switch_to.py index d7f8546..c864f7d 100644 --- a/caqui/easy/switch_to.py +++ b/caqui/easy/switch_to.py @@ -7,8 +7,8 @@ class SwitchTo: def __init__(self, driver) -> None: self._driver = driver - self._iframe :Union[str, None]= None - self._window_handle:Union[str, None] = None + self._iframe: Union[str, None] = None + self._window_handle: Union[str, None] = None self._session_http = driver.session_http @property diff --git a/caqui/easy/window.py b/caqui/easy/window.py index d8746ab..59f7912 100644 --- a/caqui/easy/window.py +++ b/caqui/easy/window.py @@ -4,7 +4,7 @@ class Window: def __init__(self, driver) -> None: self._remote = driver.remote - self._ssession = driver.session + self._session = driver.session async def new(self, window_type="tab"): """ diff --git a/caqui/helper.py b/caqui/helper.py index cecec9b..8925715 100644 --- a/caqui/helper.py +++ b/caqui/helper.py @@ -1,4 +1,5 @@ import base64 +from cssify import cssify # type: ignore def save_picture(session, path, file_name, response): @@ -20,3 +21,14 @@ def get_element(response) -> str: # Firefox return list(value.values())[0] + + +def convert_xpath_to_css_selector(locator_type: str, locator_value: str): + try: + if locator_type.lower() == "xpath": + locator_value = cssify(locator_value) + locator_type = "css selector" + except Exception: + # just ignore it and keep using the xpath selector + pass + return locator_type, locator_value diff --git a/caqui/synchronous.py b/caqui/synchronous.py index 4c3af06..e02b154 100644 --- a/caqui/synchronous.py +++ b/caqui/synchronous.py @@ -1,7 +1,7 @@ from requests import request from orjson import dumps from caqui.exceptions import WebDriverError -from caqui import helper +from caqui.helper import convert_xpath_to_css_selector, save_picture, get_element, get_elements from caqui.constants import HEADERS from typing import Any, Optional @@ -53,7 +53,7 @@ def _handle_alerts(server_url, session, command): def _handle_window(server_url, session, command): url = f"{server_url}/session/{session}/window/{command}" - payload:dict = {} + payload: dict = {} _post(url, payload) return True @@ -225,7 +225,7 @@ def take_screenshot_element(server_url, session, element, path="/tmp", file_name try: url = f"{server_url}/session/{session}/element/{element}/screenshot" response = _get(url).get("value") - helper.save_picture(session, path, file_name, response) + save_picture(session, path, file_name, response) return True except Exception as e: raise WebDriverError("Failed to take screeshot.") from e @@ -236,7 +236,7 @@ def take_screenshot(server_url, session, path="/tmp", file_name="caqui"): try: url = f"{server_url}/session/{session}/screenshot" response = _get(url).get("value") - helper.save_picture(session, path, file_name, response) + save_picture(session, path, file_name, response) return True except Exception as e: raise WebDriverError("Failed to take screeshot.") from e @@ -428,30 +428,36 @@ def set_timeouts(server_url, session, timeouts): raise WebDriverError("Failed to set timeouts.") from e -def find_children_elements(server_url, session, parent_element, locator_type, locator_value): +def find_children_elements( + server_url: str, session: str, parent_element: str, locator_type: str, locator_value: str +): """Find the children elements by 'locator_type' If the 'parent_element' is a shadow element, set the 'locator_type' as 'id' or 'css selector' """ + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: url = f"{server_url}/session/{session}/element/{parent_element}/elements" payload = {"using": locator_type, "value": locator_value, "id": parent_element} response = _post(url, payload) - return helper.get_elements(response) + return get_elements(response) except Exception as e: raise WebDriverError( f"Failed to find the children elements from '{parent_element}'." ) from e -def find_child_element(server_url, session, parent_element, locator_type, locator_value): +def find_child_element( + server_url: str, session: str, parent_element: str, locator_type: str, locator_value: str +): """Find the child element by 'locator_type'""" + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: url = f"{server_url}/session/{session}/element/{parent_element}/element" payload = {"using": locator_type, "value": locator_value, "id": parent_element} response = _post(url, payload) - return helper.get_element(response) + return get_element(response) except Exception as e: raise WebDriverError(f"Failed to find the child element from '{parent_element}'.") from e @@ -490,7 +496,7 @@ def get_active_element(server_url, session): try: url = f"{server_url}/session/{session}/element/active" response = _get(url) - return helper.get_element(response) + return get_element(response) except Exception as e: raise WebDriverError("Failed to get the active element.") from e @@ -625,8 +631,9 @@ def get_title(server_url, session) -> str: raise WebDriverError("Failed to get page title.") from e -def find_elements(server_url, session, locator_type, locator_value) -> list: +def find_elements(server_url: str, session: str, locator_type: str, locator_value: str) -> list: """Search the DOM elements by 'locator', for example, 'xpath'""" + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: url = f"{server_url}/session/{session}/elements" payload = {"using": locator_type, "value": locator_value} @@ -751,17 +758,10 @@ def get_session(server_url: str, capabilities: Optional[dict] = None): except Exception as e: raise WebDriverError("Failed to open session. Check the browser capabilities.") from e -# from cssify import cssify -def find_element(server_url, session, locator_type, locator_value) -> str: +def find_element(server_url: str, session: str, locator_type: str, locator_value: str) -> str: """Find an element by a 'locator', for example 'xpath'""" - # try: - # if locator_type.lower() == "xpath": - # locator_type = "css selector" - # locator_value = cssify(locator_value) - # except Exception: - # # just ignore it and keep using the xpath selector - # pass + locator_type, locator_value = convert_xpath_to_css_selector(locator_type, locator_value) try: url = f"{server_url}/session/{session}/element" payload = {"using": locator_type, "value": locator_value} @@ -772,7 +772,7 @@ def find_element(server_url, session, locator_type, locator_value) -> str: if response.get("value").get("error"): raise WebDriverError(f"Failed to find element. {response}") - return helper.get_element(response) + return get_element(response) except Exception as e: raise WebDriverError( f"Failed to find element by '{locator_type}'-'{locator_value}'." diff --git a/data-processed.txt b/data-processed.txt index 642f5ce..0c05da4 100644 --- a/data-processed.txt +++ b/data-processed.txt @@ -338,3 +338,55 @@ OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) ===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict({'setup': 7.11, 'duration': 28.63, 'teardown': 3.66, 'call': 17.86, 'title': '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 24.41, 'call': 15.17, 'setup': 5.56, 'title': '# Scenario 1 | No concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'teardown': 3.68, 'duration': 26.59, 'call': 17.19, 'setup': 5.72, 'title': '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'teardown': 3.66, 'duration': 24.8, 'call': 15.62, 'setup': 5.52, 'title': '# Scenario 1 | No concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 25.35, 'call': 16.26, 'setup': 5.42, 'title': '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n'}) +OrderedDict({'teardown': 3.67, 'duration': 23.94, 'call': 14.95, 'setup': 5.32, 'title': '# Scenario 1 | No concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 32.73, 'duration': 59.24, 'teardown': 12.81, 'setup': 13.7, 'title': '# Scenario 2 | With concurrence | Execution 1 | Shared session\n'}) +OrderedDict({'call': 31.9, 'duration': 62.89, 'teardown': 13.08, 'setup': 17.91, 'title': '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n'}) +OrderedDict({'call': 32.1, 'duration': 62.52, 'teardown': 13.07, 'setup': 17.35, 'title': '# Scenario 2 | With concurrence | Execution 2 | Shared session\n'}) +OrderedDict({'call': 31.25, 'duration': 61.18, 'teardown': 12.93, 'setup': 17.0, 'title': '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n'}) +OrderedDict({'call': 32.27, 'duration': 63.9, 'teardown': 13.08, 'setup': 18.55, 'title': '# Scenario 2 | With concurrence | Execution 3 | Shared session\n'}) +OrderedDict({'call': 31.81, 'duration': 61.67, 'teardown': 12.96, 'setup': 16.9, 'title': '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n'}) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== +OrderedDict([('setup', 7.11), ('duration', 28.63), ('teardown', 3.66), ('call', 17.86), ('title', '# Scenario 1 | No concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 24.41), ('call', 15.17), ('setup', 5.56), ('title', '# Scenario 1 | No concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('teardown', 3.68), ('duration', 26.59), ('call', 17.19), ('setup', 5.72), ('title', '# Scenario 1 | No concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('teardown', 3.66), ('duration', 24.8), ('call', 15.62), ('setup', 5.52), ('title', '# Scenario 1 | No concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 25.35), ('call', 16.26), ('setup', 5.42), ('title', '# Scenario 1 | No concurrence | Execution 3 | No Shared session\n')]) +OrderedDict([('teardown', 3.67), ('duration', 23.94), ('call', 14.95), ('setup', 5.32), ('title', '# Scenario 1 | No concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 32.73), ('duration', 59.24), ('teardown', 12.81), ('setup', 13.7), ('title', '# Scenario 2 | With concurrence | Execution 1 | Shared session\n')]) +OrderedDict([('call', 31.9), ('duration', 62.89), ('teardown', 13.08), ('setup', 17.91), ('title', '# Scenario 2 | With concurrence | Execution 1 | No Shared session\n')]) +OrderedDict([('call', 32.1), ('duration', 62.52), ('teardown', 13.07), ('setup', 17.35), ('title', '# Scenario 2 | With concurrence | Execution 2 | Shared session\n')]) +OrderedDict([('call', 31.25), ('duration', 61.18), ('teardown', 12.93), ('setup', 17.0), ('title', '# Scenario 2 | With concurrence | Execution 2 | No Shared session\n')]) +OrderedDict([('call', 32.27), ('duration', 63.9), ('teardown', 13.08), ('setup', 18.55), ('title', '# Scenario 2 | With concurrence | Execution 3 | Shared session\n')]) +OrderedDict([('call', 31.81), ('duration', 61.67), ('teardown', 12.96), ('setup', 16.9), ('title', '# Scenario 2 | With concurrence | Execution 3 | No Shared session\n')]) +===== diff --git a/dev-requirements.txt b/dev-requirements.txt index 9d9ca4f..6954fa1 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,4 +2,5 @@ requests aiohttp webdriver_manager types-requests -orjson \ No newline at end of file +orjson +cssify \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 1361491..6ed1655 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,9 +20,10 @@ classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python :: 3", "Operating System :: OS Independent", + "License :: OSI Approved :: MIT License", ] -license="MIT" + dependencies = [ "requests", @@ -30,6 +31,7 @@ dependencies = [ "webdriver_manager", "types-requests", "orjson", + "cssify", ] [project.urls] @@ -46,4 +48,4 @@ disallow_any_generics = false disallow_subclassing_any = false disallow_untyped_calls = false disallow_untyped_defs=false -check_untyped_defs = true \ No newline at end of file +check_untyped_defs = true diff --git a/setup.py b/setup.py index 55c9a85..eee1001 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,26 @@ - -from distutils.core import setup +from distutils.core import setup from Cython.Build import cythonize + setup( - ext_modules = cythonize( - ['./caqui/asynchronous.pyx', './caqui/constants.pyx', './caqui/helper.pyx', './caqui/exceptions.pyx', './caqui/synchronous.pyx', './caqui/by.pyx', './caqui/__init__.pyx', './caqui/easy/capabilities.pyx', './caqui/easy/options.pyx', './caqui/easy/action_chains.pyx', './caqui/easy/alert.pyx', './caqui/easy/page.pyx', './caqui/easy/switch_to.pyx', './caqui/easy/window.pyx', './caqui/easy/element.pyx', './caqui/easy/__init__.pyx', './caqui/easy/server.pyx'] + ext_modules=cythonize( + [ + "./caqui/asynchronous.pyx", + "./caqui/constants.pyx", + "./caqui/helper.pyx", + "./caqui/exceptions.pyx", + "./caqui/synchronous.pyx", + "./caqui/by.pyx", + "./caqui/__init__.pyx", + "./caqui/easy/capabilities.pyx", + "./caqui/easy/options.pyx", + "./caqui/easy/action_chains.pyx", + "./caqui/easy/alert.pyx", + "./caqui/easy/page.pyx", + "./caqui/easy/switch_to.pyx", + "./caqui/easy/window.pyx", + "./caqui/easy/element.pyx", + "./caqui/easy/__init__.pyx", + "./caqui/easy/server.pyx", + ] ) ) diff --git a/tests/conftest.py b/tests/conftest.py index 30110e2..ad84690 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,6 +20,7 @@ def setup_server(): # yield # server.dispose(delay=3) + def _build_capabilities(): options = ChromeOptionsBuilder().args(["headless"]) capabilities = ( @@ -31,7 +32,6 @@ def _build_capabilities(): return capabilities - @fixture def setup_functional_environment(): server_url = SERVER_URL diff --git a/tests/feature/test_async_with_http_session.py b/tests/feature/test_async_with_http_session.py index 1d0c8e7..fdf4e95 100644 --- a/tests/feature/test_async_with_http_session.py +++ b/tests/feature/test_async_with_http_session.py @@ -185,7 +185,7 @@ async def test_minimize_window(setup_functional_environment): assert synchronous.minimize_window(server_url, session) is True - window_rectangle_after :dict= synchronous.get_window_rectangle(server_url, session) + window_rectangle_after: dict = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before assert window_rectangle_after.get("height", 0) < window_rectangle_before.get("height", 0) assert window_rectangle_after.get("width", 0) < window_rectangle_before.get("width", 0) diff --git a/tests/feature/test_sync_and_async.py b/tests/feature/test_sync_and_async.py index b5b7e74..a40d0be 100644 --- a/tests/feature/test_sync_and_async.py +++ b/tests/feature/test_sync_and_async.py @@ -148,7 +148,7 @@ async def test_fullscreen_window(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before - assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height",0) + assert window_rectangle_after.get("height", 0) > window_rectangle_before.get("height", 0) assert window_rectangle_after.get("width", 0) > window_rectangle_before.get("width", 0) @@ -163,7 +163,7 @@ async def test_minimize_window(setup_functional_environment): window_rectangle_after = synchronous.get_window_rectangle(server_url, session) assert window_rectangle_after != window_rectangle_before assert window_rectangle_after.get("height", 0) < window_rectangle_before.get("height", 0) - assert window_rectangle_after.get("width", 0) < window_rectangle_before.get("width",0) + assert window_rectangle_after.get("width", 0) < window_rectangle_before.get("width", 0) synchronous.maximize_window(server_url, session) diff --git a/tests/unit/test_async_unit.py b/tests/unit/test_async_unit.py index 4c3c7d8..f9e74b0 100644 --- a/tests/unit/test_async_unit.py +++ b/tests/unit/test_async_unit.py @@ -186,7 +186,7 @@ async def mock_request(*args, **kwargs): @mark.asyncio async def test_close_window(): - expected:list = [] + expected: list = [] async def mock_request(*args, **kwargs): return fake_responses.CLOSE_WINDOW @@ -281,7 +281,7 @@ async def mock_request(*args, **kwargs): @mark.asyncio async def test_get_cookies(): - expected:list = [] + expected: list = [] async def mock_request(*args, **kwargs): return fake_responses.GET_COOKIES diff --git a/tests/unit/test_sync_unit.py b/tests/unit/test_sync_unit.py index 3e35eeb..8f4d04f 100644 --- a/tests/unit/test_sync_unit.py +++ b/tests/unit/test_sync_unit.py @@ -108,7 +108,7 @@ def test_get_window_handles(*args): @patch("caqui.synchronous.request", return_value=fake_responses.CLOSE_WINDOW) def test_close_window(*args): - expected:list = [] + expected: list = [] assert synchronous.close_window("", "") == expected @@ -148,7 +148,7 @@ def test_get_title(*args): @patch("caqui.synchronous.request", return_value=fake_responses.GET_COOKIES) def test_get_cookies(*args): - expected :list= [] + expected: list = [] assert synchronous.get_cookies("", "") == expected diff --git a/utils/run-mypy.sh b/utils/run-mypy.sh new file mode 100644 index 0000000..d931bd9 --- /dev/null +++ b/utils/run-mypy.sh @@ -0,0 +1 @@ +mypy caqui/ tests/ --config-file=pyproject.toml From d110ab39da2aefabe9ec43b7e5c292ea7a9a1a36 Mon Sep 17 00:00:00 2001 From: Douglas Cardoso <29078346+douglasdcm@users.noreply.github.com> Date: Mon, 1 Dec 2025 02:53:18 -0300 Subject: [PATCH 10/40] Add copyright anf put improve cssify --- caqui/__init__.py | 5 + caqui/asynchronous.py | 7 +- caqui/by.py | 5 + caqui/constants.py | 5 + caqui/cssify.py | 94 +++++++++++++++++++ caqui/easy/__init__.py | 5 + caqui/easy/action_chains.py | 5 + caqui/easy/alert.py | 5 + caqui/easy/capabilities.py | 5 + caqui/easy/element.py | 9 ++ caqui/easy/options.py | 5 + caqui/easy/page.py | 6 ++ caqui/easy/server.py | 5 + caqui/easy/switch_to.py | 5 + caqui/easy/window.py | 5 + caqui/exceptions.py | 5 + caqui/helper.py | 7 +- caqui/synchronous.py | 7 +- data-processed.txt | 26 +++++ docs/conf.py | 2 +- pyproject.toml | 51 ---------- setup.py | 35 +++---- tests/performance/README.md | 27 +++++- tests/performance/test_single_session_http.py | 2 +- utils/build-pyx-files.py | 33 +++++-- utils/run-mypy.sh | 2 +- 26 files changed, 280 insertions(+), 88 deletions(-) create mode 100644 caqui/cssify.py delete mode 100644 pyproject.toml mode change 100644 => 100755 utils/run-mypy.sh diff --git a/caqui/__init__.py b/caqui/__init__.py index e69de29..ac64211 100644 --- a/caqui/__init__.py +++ b/caqui/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2023 Caqui - All Rights Reserved +# You may use, distribute and modify this code under the +# terms of the MIT license. +# Visit: https://github.com/douglasdcm/caqui + diff --git a/caqui/asynchronous.py b/caqui/asynchronous.py index b237d53..91e6d93 100644 --- a/caqui/asynchronous.py +++ b/caqui/asynchronous.py @@ -1,8 +1,13 @@ +# Copyright (C) 2023 Caqui - All Rights Reserved +# You may use, distribute and modify this code under the +# terms of the MIT license. +# Visit: https://github.com/douglasdcm/caqui + from aiohttp import ClientSession from orjson import dumps from caqui.constants import HEADERS from caqui.exceptions import WebDriverError -from caqui.helper import convert_xpath_to_css_selector, save_picture, get_elements, get_element +from caqui.helper import save_picture, get_elements, get_element, convert_xpath_to_css_selector from typing import Any, List, Optional, Union diff --git a/caqui/by.py b/caqui/by.py index d87b2c0..89e52f2 100644 --- a/caqui/by.py +++ b/caqui/by.py @@ -1,3 +1,8 @@ +# Copyright (C) 2023 Caqui - All Rights Reserved +# You may use, distribute and modify this code under the +# terms of the MIT license. +# Visit: https://github.com/douglasdcm/caqui + class By: """List of locator strategies""" diff --git a/caqui/constants.py b/caqui/constants.py index ded3e77..9fae696 100644 --- a/caqui/constants.py +++ b/caqui/constants.py @@ -1,3 +1,8 @@ +# Copyright (C) 2023 Caqui - All Rights Reserved +# You may use, distribute and modify this code under the +# terms of the MIT license. +# Visit: https://github.com/douglasdcm/caqui + HEADERS = { "Accept-Encoding": "identity", "Accept": "application/json", diff --git a/caqui/cssify.py b/caqui/cssify.py new file mode 100644 index 0000000..5a94652 --- /dev/null +++ b/caqui/cssify.py @@ -0,0 +1,94 @@ +# Copyright (c) 2025 Santiycr +# +# This file is part of Santiycr/cssify. +# Visit: https://github.com/santiycr/cssify +# +# Copyright (C) 2025 Caqui - All Rights Reserved +# You may use, distribute and modify this code under the +# terms of the MIT license. +# Visit: https://github.com/douglasdcm/caqui + +import re + +sub_regexes = { + "tag": r"([a-zA-Z][a-zA-Z0-9]{0,10}|\*)", + "attribute": r"[.a-zA-Z_:][-\w:.]*(\(\))?)", + "value": r"\s*[\w/:][-/\w\s,:;.]*", +} + +validation_re = ( + "(?P" + "(" + "^id\\([\"']?(?P%(value)s)[\"']?\\)" # special case! id(idValue) + "|" + "(?P