From 9b35e148fe776a63f29dbfe6c35d16d1a752b120 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Wed, 1 Sep 2021 14:09:14 +0200 Subject: [PATCH] chore: annotate sync tests with types --- .pre-commit-config.yaml | 1 + local-requirements.txt | 1 + playwright/_impl/_locator.py | 2 +- playwright/async_api/_generated.py | 6 +- playwright/sync_api/_generated.py | 6 +- setup.cfg | 2 +- tests/async/test_element_handle.py | 3 +- tests/common/test_threads.py | 5 +- tests/conftest.py | 60 ++--- tests/server.py | 61 ++--- tests/sync/conftest.py | 29 ++- tests/sync/test_accessibility.py | 84 ++++--- tests/sync/test_add_init_script.py | 38 ++-- .../sync/test_browsercontext_storage_state.py | 12 +- tests/sync/test_browsertype_connect.py | 42 ++-- tests/sync/test_browsertype_connect_cdp.py | 2 +- tests/sync/test_cdp_session.py | 13 +- tests/sync/test_check.py | 20 +- tests/sync/test_console.py | 27 ++- tests/sync/test_context_manager.py | 4 +- tests/sync/test_element_handle.py | 212 ++++++++++++------ ...t_element_handle_wait_for_element_state.py | 35 ++- tests/sync/test_har.py | 10 +- tests/sync/test_input.py | 5 +- tests/sync/test_listeners.py | 8 +- tests/sync/test_locators.py | 89 ++++---- tests/sync/test_network.py | 17 +- tests/sync/test_page.py | 10 +- tests/sync/test_pdf.py | 4 +- tests/sync/test_resource_timing.py | 15 +- tests/sync/test_sync.py | 59 ++--- tests/sync/test_tap.py | 15 +- tests/sync/test_tracing.py | 7 +- tests/sync/test_video.py | 41 +++- tests/sync/utils.py | 27 ++- tests/test_generation_scripts.py | 4 +- tests/test_installation.py | 2 +- tests/test_reference_count_async.py | 2 +- 38 files changed, 608 insertions(+), 372 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fbe788ea0..ce1f3afa4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,6 +17,7 @@ repos: rev: v0.910 hooks: - id: mypy + additional_dependencies: [types-pyOpenSSL==20.0.6] - repo: https://gitlab.com/pycqa/flake8 rev: 3.9.2 hooks: diff --git a/local-requirements.txt b/local-requirements.txt index f242d0199..4e2b1a428 100644 --- a/local-requirements.txt +++ b/local-requirements.txt @@ -21,4 +21,5 @@ service_identity==21.1.0 setuptools==57.4.0 twine==3.4.2 twisted==21.7.0 +types-pyOpenSSL==20.0.6 wheel==0.37.0 diff --git a/playwright/_impl/_locator.py b/playwright/_impl/_locator.py index 362d30b45..71a1ee262 100644 --- a/playwright/_impl/_locator.py +++ b/playwright/_impl/_locator.py @@ -141,7 +141,7 @@ async def evaluate( timeout, ) - async def evaluate_all(self, expression: str, arg: Serializable = None) -> None: + async def evaluate_all(self, expression: str, arg: Serializable = None) -> Any: params = locals_to_params(locals()) return await self._frame.eval_on_selector_all(self._selector, **params) diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index c885bd694..95a51d3a4 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -11338,7 +11338,7 @@ async def evaluate( ) ) - async def evaluate_all(self, expression: str, arg: typing.Any = None) -> NoneType: + async def evaluate_all(self, expression: str, arg: typing.Any = None) -> typing.Any: """Locator.evaluate_all The method finds all elements matching the specified locator and passes an array of matched elements as a first argument @@ -11361,6 +11361,10 @@ async def evaluate_all(self, expression: str, arg: typing.Any = None) -> NoneTyp as a function. Otherwise, evaluated as an expression. arg : Union[Any, NoneType] Optional argument to pass to `expression`. + + Returns + ------- + Any """ return mapping.from_maybe_impl( diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index b715d1994..faf09f003 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -11268,7 +11268,7 @@ def evaluate( ) ) - def evaluate_all(self, expression: str, arg: typing.Any = None) -> NoneType: + def evaluate_all(self, expression: str, arg: typing.Any = None) -> typing.Any: """Locator.evaluate_all The method finds all elements matching the specified locator and passes an array of matched elements as a first argument @@ -11291,6 +11291,10 @@ def evaluate_all(self, expression: str, arg: typing.Any = None) -> NoneType: as a function. Otherwise, evaluated as an expression. arg : Union[Any, NoneType] Optional argument to pass to `expression`. + + Returns + ------- + Any """ return mapping.from_maybe_impl( diff --git a/setup.cfg b/setup.cfg index 47e20a6fc..669f82642 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,7 @@ warn_redundant_casts = True warn_unused_configs = True check_untyped_defs = True disallow_untyped_defs = True -[mypy-tests.*] +[mypy-tests/async.*] ignore_errors = True [flake8] ignore = diff --git a/tests/async/test_element_handle.py b/tests/async/test_element_handle.py index d680f44b4..1fbb1f415 100644 --- a/tests/async/test_element_handle.py +++ b/tests/async/test_element_handle.py @@ -70,9 +70,10 @@ async def test_bounding_box_with_SVG_nodes(page, server): assert pw_bounding_box == web_bounding_box +@pytest.mark.skip_browser("firefox") async def test_bounding_box_with_page_scale(browser, server): context = await browser.new_context( - viewport={"width": 400, "height": 400, "is_mobile": True} + viewport={"width": 400, "height": 400}, is_mobile=True ) page = await context.new_page() await page.goto(server.PREFIX + "/input/button.html") diff --git a/tests/common/test_threads.py b/tests/common/test_threads.py index f8181f03b..3d72c60a7 100644 --- a/tests/common/test_threads.py +++ b/tests/common/test_threads.py @@ -13,15 +13,16 @@ # limitations under the License. import threading +from typing import Dict from playwright.sync_api import sync_playwright -def test_running_in_thread(browser_name, launch_arguments): +def test_running_in_thread(browser_name: str, launch_arguments: Dict) -> None: result = [] class TestThread(threading.Thread): - def run(self): + def run(self) -> None: with sync_playwright() as playwright: browser = getattr(playwright, browser_name).launch(**launch_arguments) # This should not throw ^^. diff --git a/tests/conftest.py b/tests/conftest.py index 44506f9c1..4bd1d5a4c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,7 +19,7 @@ import subprocess import sys from pathlib import Path -from typing import Dict, List +from typing import Any, AsyncGenerator, Callable, Dict, Generator, List import pytest from PIL import Image @@ -29,31 +29,31 @@ import playwright from playwright._impl._path_utils import get_file_dirname -from .server import test_server +from .server import Server, WebSocketServerServer, test_server _dirname = get_file_dirname() -def pytest_generate_tests(metafunc): +def pytest_generate_tests(metafunc: Any) -> None: if "browser_name" in metafunc.fixturenames: browsers = metafunc.config.option.browser or ["chromium", "firefox", "webkit"] metafunc.parametrize("browser_name", browsers, scope="session") @pytest.fixture(scope="session") -def event_loop(): +def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]: loop = asyncio.get_event_loop() yield loop loop.close() @pytest.fixture(scope="session") -def assetdir(): +def assetdir() -> Path: return _dirname / "assets" @pytest.fixture(scope="session") -def launch_arguments(pytestconfig): +def launch_arguments(pytestconfig: Any) -> Dict: return { "headless": not pytestconfig.getoption("--headful"), "channel": pytestconfig.getoption("--browser-channel"), @@ -61,74 +61,76 @@ def launch_arguments(pytestconfig): @pytest.fixture -def server(): +def server() -> Generator[Server, None, None]: yield test_server.server @pytest.fixture -def https_server(): +def https_server() -> Generator[Server, None, None]: yield test_server.https_server @pytest.fixture -def ws_server(): +def ws_server() -> Generator[WebSocketServerServer, None, None]: yield test_server.ws_server @pytest.fixture(autouse=True, scope="session") -async def start_server(): +async def start_server() -> AsyncGenerator[None, None]: test_server.start() yield test_server.stop() @pytest.fixture(autouse=True) -def after_each_hook(): +def after_each_hook() -> Generator[None, None, None]: yield test_server.reset() @pytest.fixture(scope="session") -def browser_name(pytestconfig): +def browser_name(pytestconfig: Any) -> None: return pytestconfig.getoption("browser") @pytest.fixture(scope="session") -def browser_channel(pytestconfig): +def browser_channel(pytestconfig: Any) -> None: return pytestconfig.getoption("--browser-channel") @pytest.fixture(scope="session") -def is_webkit(browser_name): +def is_webkit(browser_name: str) -> bool: return browser_name == "webkit" @pytest.fixture(scope="session") -def is_firefox(browser_name): +def is_firefox(browser_name: str) -> bool: return browser_name == "firefox" @pytest.fixture(scope="session") -def is_chromium(browser_name): +def is_chromium(browser_name: str) -> bool: return browser_name == "chromium" @pytest.fixture(scope="session") -def is_win(): +def is_win() -> bool: return sys.platform == "win32" @pytest.fixture(scope="session") -def is_linux(): +def is_linux() -> bool: return sys.platform == "linux" @pytest.fixture(scope="session") -def is_mac(): +def is_mac() -> bool: return sys.platform == "darwin" -def _get_skiplist(request, values, value_name): +def _get_skiplist( + request: pytest.FixtureRequest, values: List[str], value_name: str +) -> List[str]: skipped_values = [] # Allowlist only_marker = request.node.get_closest_marker(f"only_{value_name}") @@ -145,7 +147,7 @@ def _get_skiplist(request, values, value_name): @pytest.fixture(autouse=True) -def skip_by_browser(request, browser_name): +def skip_by_browser(request: pytest.FixtureRequest, browser_name: str) -> None: skip_browsers_names = _get_skiplist( request, ["chromium", "firefox", "webkit"], "browser" ) @@ -155,7 +157,7 @@ def skip_by_browser(request, browser_name): @pytest.fixture(autouse=True) -def skip_by_platform(request): +def skip_by_platform(request: pytest.FixtureRequest) -> None: skip_platform_names = _get_skiplist( request, ["win32", "linux", "darwin"], "platform" ) @@ -164,7 +166,7 @@ def skip_by_platform(request): pytest.skip(f"skipped on this platform: {sys.platform}") -def pytest_addoption(parser): +def pytest_addoption(parser: Any) -> None: group = parser.getgroup("playwright", "Playwright") group.addoption( "--browser", @@ -187,8 +189,8 @@ def pytest_addoption(parser): @pytest.fixture(scope="session") -def assert_to_be_golden(browser_name: str): - def compare(received_raw: bytes, golden_name: str): +def assert_to_be_golden(browser_name: str) -> Callable[[bytes, str], None]: + def compare(received_raw: bytes, golden_name: str) -> None: golden_file = (_dirname / f"golden-{browser_name}" / golden_name).read_bytes() received_image = Image.open(io.BytesIO(received_raw)) golden_image = Image.open(io.BytesIO(golden_file)) @@ -235,7 +237,7 @@ def __init__( self.ws_endpoint = self.process.stdout.readline().decode().strip() self.process.stdout.close() - def kill(self): + def kill(self) -> None: # Send the signal to all the process groups if self.process.poll() is not None: return @@ -244,10 +246,12 @@ def kill(self): @pytest.fixture -def launch_server(browser_name: str, launch_arguments: Dict, tmp_path: Path): +def launch_server( + browser_name: str, launch_arguments: Dict, tmp_path: Path +) -> Generator[Callable[..., RemoteServer], None, None]: remotes: List[RemoteServer] = [] - def _launch_server(**kwargs: Dict): + def _launch_server(**kwargs: Dict[str, Any]) -> RemoteServer: remote = RemoteServer( browser_name, { diff --git a/tests/server.py b/tests/server.py index dbc517628..921774b69 100644 --- a/tests/server.py +++ b/tests/server.py @@ -20,11 +20,13 @@ import threading from contextlib import closing from http import HTTPStatus +from typing import Any, Callable, Dict, Set, Tuple from urllib.parse import urlparse from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol from OpenSSL import crypto from twisted.internet import reactor, ssl +from twisted.internet.protocol import ClientFactory from twisted.web import http from playwright._impl._path_utils import get_file_dirname @@ -32,7 +34,7 @@ _dirname = get_file_dirname() -def find_free_port(): +def find_free_port() -> int: with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: s.bind(("", 0)) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -42,7 +44,7 @@ def find_free_port(): class Server: protocol = "http" - def __init__(self): + def __init__(self) -> None: self.PORT = find_free_port() self.EMPTY_PAGE = f"{self.protocol}://localhost:{self.PORT}/empty.html" self.PREFIX = f"{self.protocol}://localhost:{self.PORT}" @@ -58,15 +60,15 @@ def __repr__(self) -> str: return self.PREFIX @abc.abstractmethod - def listen(self, factory): + def listen(self, factory: ClientFactory) -> None: pass - def start(self): - request_subscribers = {} - auth = {} - csp = {} - routes = {} - gzip_routes = set() + def start(self) -> None: + request_subscribers: Dict[str, asyncio.Future] = {} + auth: Dict[str, Tuple[str, str]] = {} + csp: Dict[str, str] = {} + routes: Dict[str, Callable[[http.Request], Any]] = {} + gzip_routes: Set[str] = set() self.request_subscribers = request_subscribers self.auth = auth self.csp = csp @@ -75,10 +77,13 @@ def start(self): static_path = _dirname / "assets" class TestServerHTTPHandler(http.Request): - def process(self): + def process(self) -> None: request = self - self.post_body = request.content.read() - request.content.seek(0, 0) + if request.content: + self.post_body = request.content.read() + request.content.seek(0, 0) + else: + self.post_body = None uri = urlparse(request.uri.decode()) path = uri.path @@ -131,34 +136,34 @@ class MyHttpFactory(http.HTTPFactory): self.listen(MyHttpFactory()) - async def wait_for_request(self, path): + async def wait_for_request(self, path: str) -> http.Request: if path in self.request_subscribers: return await self.request_subscribers[path] - future = asyncio.Future() + future: asyncio.Future["http.Request"] = asyncio.Future() self.request_subscribers[path] = future return await future - def set_auth(self, path: str, username: str, password: str): + def set_auth(self, path: str, username: str, password: str) -> None: self.auth[path] = (username, password) - def set_csp(self, path: str, value: str): + def set_csp(self, path: str, value: str) -> None: self.csp[path] = value - def reset(self): + def reset(self) -> None: self.request_subscribers.clear() self.auth.clear() self.csp.clear() self.gzip_routes.clear() self.routes.clear() - def set_route(self, path, callback): + def set_route(self, path: str, callback: Callable[[http.Request], Any]) -> None: self.routes[path] = callback - def enable_gzip(self, path): + def enable_gzip(self, path: str) -> None: self.gzip_routes.add(path) - def set_redirect(self, from_, to): - def handle_redirect(request): + def set_redirect(self, from_: str, to: str) -> None: + def handle_redirect(request: http.Request) -> None: request.setResponseCode(HTTPStatus.FOUND) request.setHeader("location", to) request.finish() @@ -167,14 +172,14 @@ def handle_redirect(request): class HTTPServer(Server): - def listen(self, factory): + def listen(self, factory: ClientFactory) -> None: reactor.listenTCP(self.PORT, factory) class HTTPSServer(Server): protocol = "https" - def listen(self, factory): + def listen(self, factory: ClientFactory) -> None: cert = ssl.PrivateCertificate.fromCertificateAndKeyPair( ssl.Certificate.loadPEM( (_dirname / "testserver" / "cert.pem").read_bytes() @@ -192,20 +197,20 @@ def __init__(self) -> None: super().__init__() self.PORT = find_free_port() - def start(self): + def start(self) -> None: ws = WebSocketServerFactory("ws://127.0.0.1:" + str(self.PORT)) ws.protocol = WebSocketProtocol reactor.listenTCP(self.PORT, ws) class WebSocketProtocol(WebSocketServerProtocol): - def onConnect(self, request): + def onConnect(self, request: Any) -> None: pass - def onOpen(self): + def onOpen(self) -> None: self.sendMessage(b"incoming") - def onMessage(self, payload, isBinary): + def onMessage(self, payload: bytes, isBinary: bool) -> None: if payload == b"echo-bin": self.sendMessage(b"\x04\x02", True) self.sendClose() @@ -215,7 +220,7 @@ def onMessage(self, payload, isBinary): if payload == b"close": self.sendClose() - def onClose(self, wasClean, code, reason): + def onClose(self, wasClean: Any, code: Any, reason: Any) -> None: pass diff --git a/tests/sync/conftest.py b/tests/sync/conftest.py index 8e6ccbf4d..868729cee 100644 --- a/tests/sync/conftest.py +++ b/tests/sync/conftest.py @@ -13,26 +13,38 @@ # limitations under the License. +from typing import Dict, Generator + import pytest -from playwright.sync_api import sync_playwright +from playwright.sync_api import ( + Browser, + BrowserContext, + BrowserType, + Page, + Playwright, + sync_playwright, +) +from .utils import Utils from .utils import utils as utils_object @pytest.fixture -def utils(): +def utils() -> Generator[Utils, None, None]: yield utils_object @pytest.fixture(scope="session") -def playwright(): +def playwright() -> Generator[Playwright, None, None]: with sync_playwright() as p: yield p @pytest.fixture(scope="session") -def browser_type(playwright, browser_name): +def browser_type( + playwright: Playwright, browser_name: str +) -> Generator[BrowserType, None, None]: browser_type = None if browser_name == "chromium": browser_type = playwright.chromium @@ -40,25 +52,28 @@ def browser_type(playwright, browser_name): browser_type = playwright.firefox elif browser_name == "webkit": browser_type = playwright.webkit + assert browser_type yield browser_type @pytest.fixture(scope="session") -def browser(browser_type, launch_arguments): +def browser( + browser_type: BrowserType, launch_arguments: Dict +) -> Generator[Browser, None, None]: browser = browser_type.launch(**launch_arguments) yield browser browser.close() @pytest.fixture -def context(browser): +def context(browser: Browser) -> Generator[BrowserContext, None, None]: context = browser.new_context() yield context context.close() @pytest.fixture -def page(context): +def page(context: BrowserContext) -> Generator[Page, None, None]: page = context.new_page() yield page page.close() diff --git a/tests/sync/test_accessibility.py b/tests/sync/test_accessibility.py index f45c1ece8..02daa1da1 100644 --- a/tests/sync/test_accessibility.py +++ b/tests/sync/test_accessibility.py @@ -14,8 +14,12 @@ import pytest +from playwright.sync_api import Page -def test_accessibility_should_work(page, is_firefox, is_chromium): + +def test_accessibility_should_work( + page: Page, is_firefox: bool, is_chromium: bool +) -> None: page.set_content( """ Accessibility Test @@ -101,48 +105,56 @@ def test_accessibility_should_work(page, is_firefox, is_chromium): assert page.accessibility.snapshot() == golden -def test_accessibility_should_work_with_regular_text(page, is_firefox): +def test_accessibility_should_work_with_regular_text( + page: Page, is_firefox: bool +) -> None: page.set_content("
Hello World
") snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == { "role": "text leaf" if is_firefox else "text", "name": "Hello World", } -def test_accessibility_roledescription(page): +def test_accessibility_roledescription(page: Page) -> None: page.set_content('
Hi
') snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0]["roledescription"] == "foo" -def test_accessibility_orientation(page): +def test_accessibility_orientation(page: Page) -> None: page.set_content('11') snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0]["orientation"] == "vertical" -def test_accessibility_autocomplete(page): +def test_accessibility_autocomplete(page: Page) -> None: page.set_content('
hi
') snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0]["autocomplete"] == "list" -def test_accessibility_multiselectable(page): +def test_accessibility_multiselectable(page: Page) -> None: page.set_content('
hey
') snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0]["multiselectable"] -def test_accessibility_keyshortcuts(page): +def test_accessibility_keyshortcuts(page: Page) -> None: page.set_content('
hey
') snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0]["keyshortcuts"] == "foo" def test_accessibility_filtering_children_of_leaf_nodes_should_not_report_text_nodes_inside_controls( - page, is_firefox -): + page: Page, is_firefox: bool +) -> None: page.set_content( """
@@ -164,8 +176,8 @@ def test_accessibility_filtering_children_of_leaf_nodes_should_not_report_text_n # WebKit rich text accessibility is iffy @pytest.mark.skip_browser("webkit") def test_accessibility_filtering_children_of_leaf_nodes_rich_text_editable_fields_should_have_children( - page, is_firefox -): + page: Page, is_firefox: bool +) -> None: page.set_content( """
@@ -193,14 +205,15 @@ def test_accessibility_filtering_children_of_leaf_nodes_rich_text_editable_field } ) snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == golden # WebKit rich text accessibility is iffy @pytest.mark.skip_browser("webkit") def test_accessibility_filtering_children_of_leaf_nodes_rich_text_editable_fields_with_role_should_have_children( - page, is_firefox, browser_channel -): + page: Page, is_firefox: bool +) -> None: page.set_content( """
@@ -227,6 +240,7 @@ def test_accessibility_filtering_children_of_leaf_nodes_rich_text_editable_field "value": "Edit this image: ", } snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == golden @@ -234,13 +248,14 @@ def test_accessibility_filtering_children_of_leaf_nodes_rich_text_editable_field # WebKit rich text accessibility is iffy @pytest.mark.only_browser("chromium") def test_accessibility_plain_text_field_with_role_should_not_have_children( - page, browser_channel -): + page: Page, +) -> None: page.set_content( """
Edit this image:my fake image
""" ) snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == { "multiline": True, "name": "", @@ -251,13 +266,14 @@ def test_accessibility_plain_text_field_with_role_should_not_have_children( @pytest.mark.only_browser("chromium") def test_accessibility_plain_text_field_without_role_should_not_have_content( - page, browser_channel -): + page: Page, +) -> None: page.set_content( """
Edit this image:my fake image
""" ) snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == { "name": "", "role": "generic", @@ -267,13 +283,14 @@ def test_accessibility_plain_text_field_without_role_should_not_have_content( @pytest.mark.only_browser("chromium") def test_accessibility_plain_text_field_with_tabindex_and_without_role_should_not_have_content( - page, browser_channel -): + page: Page, +) -> None: page.set_content( """
Edit this image:my fake image
""" ) snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == { "name": "", "role": "generic", @@ -282,8 +299,8 @@ def test_accessibility_plain_text_field_with_tabindex_and_without_role_should_no def test_accessibility_non_editable_textbox_with_role_and_tabIndex_and_label_should_not_have_children( - page, is_chromium, is_firefox -): + page: Page, is_chromium: bool, is_firefox: bool +) -> None: page.set_content( """
@@ -310,12 +327,13 @@ def test_accessibility_non_editable_textbox_with_role_and_tabIndex_and_label_sho "value": "this is the inner content ", } snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == golden def test_accessibility_checkbox_with_and_tabIndex_and_label_should_not_have_children( - page, -): + page: Page, +) -> None: page.set_content( """
@@ -325,12 +343,13 @@ def test_accessibility_checkbox_with_and_tabIndex_and_label_should_not_have_chil ) golden = {"role": "checkbox", "name": "my favorite checkbox", "checked": True} snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == golden def test_accessibility_checkbox_without_label_should_not_have_children( - page, is_firefox -): + page: Page, +) -> None: page.set_content( """
@@ -344,10 +363,11 @@ def test_accessibility_checkbox_without_label_should_not_have_children( "checked": True, } snapshot = page.accessibility.snapshot() + assert snapshot assert snapshot["children"][0] == golden -def test_accessibility_should_work_a_button(page): +def test_accessibility_should_work_a_button(page: Page) -> None: page.set_content("") button = page.query_selector("button") @@ -357,7 +377,7 @@ def test_accessibility_should_work_a_button(page): } -def test_accessibility_should_work_an_input(page): +def test_accessibility_should_work_an_input(page: Page) -> None: page.set_content('') input = page.query_selector("input") @@ -368,7 +388,7 @@ def test_accessibility_should_work_an_input(page): } -def test_accessibility_should_work_on_a_menu(page, is_webkit): +def test_accessibility_should_work_on_a_menu(page: Page, is_webkit: bool) -> None: page.set_content( """
@@ -395,15 +415,15 @@ def test_accessibility_should_work_on_a_menu(page, is_webkit): def test_accessibility_should_return_null_when_the_element_is_no_longer_in_DOM( - page, -): + page: Page, +) -> None: page.set_content("") button = page.query_selector("button") page.eval_on_selector("button", "button => button.remove()") assert page.accessibility.snapshot(root=button) is None -def test_accessibility_should_show_uninteresting_nodes(page): +def test_accessibility_should_show_uninteresting_nodes(page: Page) -> None: page.set_content( """
@@ -418,7 +438,9 @@ def test_accessibility_should_show_uninteresting_nodes(page): ) root = page.query_selector("#root") + assert root snapshot = page.accessibility.snapshot(root=root, interesting_only=False) + assert snapshot assert snapshot["role"] == "textbox" assert "hello" in snapshot["value"] assert "world" in snapshot["value"] diff --git a/tests/sync/test_add_init_script.py b/tests/sync/test_add_init_script.py index 48308e541..5fc626a18 100644 --- a/tests/sync/test_add_init_script.py +++ b/tests/sync/test_add_init_script.py @@ -12,37 +12,41 @@ # See the License for the specific language governing permissions and # limitations under the License. -from playwright.sync_api import Error, Page +from pathlib import Path +import pytest -def test_add_init_script_evaluate_before_anything_else_on_the_page(page): +from playwright.sync_api import BrowserContext, Error, Page + + +def test_add_init_script_evaluate_before_anything_else_on_the_page(page: Page) -> None: page.add_init_script("window.injected = 123") page.goto("data:text/html,") assert page.evaluate("window.result") == 123 -def test_add_init_script_work_with_a_path(page, assetdir): +def test_add_init_script_work_with_a_path(page: Page, assetdir: Path) -> None: page.add_init_script(path=assetdir / "injectedfile.js") page.goto("data:text/html,") assert page.evaluate("window.result") == 123 -def test_add_init_script_work_with_content(page: Page): +def test_add_init_script_work_with_content(page: Page) -> None: page.add_init_script("window.injected = 123") page.goto("data:text/html,") assert page.evaluate("window.result") == 123 -def test_add_init_script_throw_without_path_and_content(page): - error = None - try: - page.add_init_script({"foo": "bar"}) - except Error as e: - error = e - assert error.message == "Either path or script parameter must be specified" +def test_add_init_script_throw_without_path_and_content(page: Page) -> None: + with pytest.raises( + Error, match="Either path or script parameter must be specified" + ): + page.add_init_script({"foo": "bar"}) # type: ignore -def test_add_init_script_work_with_browser_context_scripts(page, context): +def test_add_init_script_work_with_browser_context_scripts( + page: Page, context: BrowserContext +) -> None: context.add_init_script("window.temp = 123") page = context.new_page() page.add_init_script("window.injected = window.temp") @@ -51,8 +55,8 @@ def test_add_init_script_work_with_browser_context_scripts(page, context): def test_add_init_script_work_with_browser_context_scripts_with_a_path( - page, context, assetdir -): + page: Page, context: BrowserContext, assetdir: Path +) -> None: context.add_init_script(path=assetdir / "injectedfile.js") page = context.new_page() page.goto("data:text/html,") @@ -60,15 +64,15 @@ def test_add_init_script_work_with_browser_context_scripts_with_a_path( def test_add_init_script_work_with_browser_context_scripts_for_already_created_pages( - page, context -): + page: Page, context: BrowserContext +) -> None: context.add_init_script("window.temp = 123") page.add_init_script("window.injected = window.temp") page.goto("data:text/html,") assert page.evaluate("window.result") == 123 -def test_add_init_script_support_multiple_scripts(page): +def test_add_init_script_support_multiple_scripts(page: Page) -> None: page.add_init_script("window.script1 = 1") page.add_init_script("window.script2 = 2") page.goto("data:text/html,") diff --git a/tests/sync/test_browsercontext_storage_state.py b/tests/sync/test_browsercontext_storage_state.py index 3b8149fc6..fc901a5cf 100644 --- a/tests/sync/test_browsercontext_storage_state.py +++ b/tests/sync/test_browsercontext_storage_state.py @@ -13,9 +13,12 @@ # limitations under the License. import json +from pathlib import Path +from playwright.sync_api import Browser, BrowserContext -def test_should_capture_local_storage(context, is_webkit, is_win): + +def test_should_capture_local_storage(context: BrowserContext) -> None: page1 = context.new_page() page1.route("**/*", lambda route: route.fulfill(body="")) page1.goto("https://www.example.com") @@ -25,6 +28,7 @@ def test_should_capture_local_storage(context, is_webkit, is_win): state = context.storage_state() origins = state["origins"] + assert origins assert len(origins) == 2 assert origins[0] == { "origin": "https://www.example.com", @@ -36,7 +40,7 @@ def test_should_capture_local_storage(context, is_webkit, is_win): } -def test_should_set_local_storage(browser, is_webkit, is_win): +def test_should_set_local_storage(browser: Browser) -> None: context = browser.new_context( storage_state={ "origins": [ @@ -56,7 +60,9 @@ def test_should_set_local_storage(browser, is_webkit, is_win): context.close() -def test_should_round_trip_through_the_file(browser, context, tmpdir): +def test_should_round_trip_through_the_file( + browser: Browser, context: BrowserContext, tmpdir: Path +) -> None: page1 = context.new_page() page1.route( "**/*", diff --git a/tests/sync/test_browsertype_connect.py b/tests/sync/test_browsertype_connect.py index c1a98a96c..6dc84f551 100644 --- a/tests/sync/test_browsertype_connect.py +++ b/tests/sync/test_browsertype_connect.py @@ -13,16 +13,18 @@ # limitations under the License. import time +from typing import Callable import pytest from playwright.sync_api import BrowserType, Error +from tests.conftest import RemoteServer from tests.server import Server def test_browser_type_connect_slow_mo( - server: Server, browser_type: BrowserType, launch_server -): + server: Server, browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: remote_server = launch_server() browser = browser_type.connect(remote_server.ws_endpoint, slow_mo=100) browser_context = browser.new_context() @@ -35,8 +37,8 @@ def test_browser_type_connect_slow_mo( def test_browser_type_connect_should_be_able_to_reconnect_to_a_browser( - server: Server, browser_type: BrowserType, launch_server -): + server: Server, browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: remote_server = launch_server() browser = browser_type.connect(remote_server.ws_endpoint) browser_context = browser.new_context() @@ -55,8 +57,8 @@ def test_browser_type_connect_should_be_able_to_reconnect_to_a_browser( def test_browser_type_connect_should_be_able_to_connect_two_browsers_at_the_same_time( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: remote_server = launch_server() browser1 = browser_type.connect(remote_server.ws_endpoint) assert len(browser1.contexts) == 0 @@ -78,8 +80,8 @@ def test_browser_type_connect_should_be_able_to_connect_two_browsers_at_the_same def test_browser_type_connect_disconnected_event_should_be_emitted_when_browser_is_closed_or_server_is_closed( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: # Launch another server to not affect other tests. remote = launch_server() @@ -107,8 +109,8 @@ def test_browser_type_connect_disconnected_event_should_be_emitted_when_browser_ def test_browser_type_disconnected_event_should_have_browser_as_argument( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: remote_server = launch_server() browser = browser_type.connect(remote_server.ws_endpoint) event_payloads = [] @@ -118,8 +120,8 @@ def test_browser_type_disconnected_event_should_have_browser_as_argument( def test_browser_type_connect_set_browser_connected_state( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: remote_server = launch_server() browser = browser_type.connect(remote_server.ws_endpoint) assert browser.is_connected() @@ -128,8 +130,8 @@ def test_browser_type_connect_set_browser_connected_state( def test_browser_type_connect_should_throw_when_used_after_is_connected_returns_false( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: remote_server = launch_server() browser = browser_type.connect(remote_server.ws_endpoint) page = browser.new_page() @@ -143,8 +145,8 @@ def test_browser_type_connect_should_throw_when_used_after_is_connected_returns_ def test_browser_type_connect_should_forward_close_events_to_pages( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: # Launch another server to not affect other tests. remote = launch_server() @@ -164,8 +166,8 @@ def test_browser_type_connect_should_forward_close_events_to_pages( def test_browser_type_connect_should_forward_close_events_on_remote_kill( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: # Launch another server to not affect other tests. remote = launch_server() @@ -184,8 +186,8 @@ def test_browser_type_connect_should_forward_close_events_on_remote_kill( def test_connect_to_closed_server_without_hangs( - browser_type: BrowserType, launch_server -): + browser_type: BrowserType, launch_server: Callable[[], RemoteServer] +) -> None: remote_server = launch_server() remote_server.kill() with pytest.raises(Error) as exc: diff --git a/tests/sync/test_browsertype_connect_cdp.py b/tests/sync/test_browsertype_connect_cdp.py index dc579f2fc..d10f10186 100644 --- a/tests/sync/test_browsertype_connect_cdp.py +++ b/tests/sync/test_browsertype_connect_cdp.py @@ -24,7 +24,7 @@ def test_connect_to_an_existing_cdp_session( launch_arguments: Dict, browser_type: BrowserType -): +) -> None: port = find_free_port() browser_server = browser_type.launch( **launch_arguments, args=[f"--remote-debugging-port={port}"] diff --git a/tests/sync/test_cdp_session.py b/tests/sync/test_cdp_session.py index deae8d0da..c07dcb85f 100644 --- a/tests/sync/test_cdp_session.py +++ b/tests/sync/test_cdp_session.py @@ -14,11 +14,12 @@ import pytest -from playwright.sync_api import Error +from playwright.sync_api import Browser, Error, Page +from tests.server import Server @pytest.mark.only_browser("chromium") -def test_should_work(page): +def test_should_work(page: Page) -> None: client = page.context.new_cdp_session(page) events = [] client.on("Runtime.consoleAPICalled", lambda params: events.append(params)) @@ -34,7 +35,7 @@ def test_should_work(page): @pytest.mark.only_browser("chromium") -def test_should_receive_events(page, server): +def test_should_receive_events(page: Page, server: Server) -> None: client = page.context.new_cdp_session(page) client.send("Network.enable") events = [] @@ -44,7 +45,7 @@ def test_should_receive_events(page, server): @pytest.mark.only_browser("chromium") -def test_should_be_able_to_detach_session(page): +def test_should_be_able_to_detach_session(page: Page) -> None: client = page.context.new_cdp_session(page) client.send("Runtime.enable") eval_response = client.send( @@ -58,7 +59,7 @@ def test_should_be_able_to_detach_session(page): @pytest.mark.only_browser("chromium") -def test_should_not_break_page_close(browser): +def test_should_not_break_page_close(browser: Browser) -> None: context = browser.new_context() page = context.new_page() session = page.context.new_cdp_session(page) @@ -68,7 +69,7 @@ def test_should_not_break_page_close(browser): @pytest.mark.only_browser("chromium") -def test_should_detach_when_page_closes(browser): +def test_should_detach_when_page_closes(browser: Browser) -> None: context = browser.new_context() page = context.new_page() session = context.new_cdp_session(page) diff --git a/tests/sync/test_check.py b/tests/sync/test_check.py index cd2b28b3f..1394664f9 100644 --- a/tests/sync/test_check.py +++ b/tests/sync/test_check.py @@ -12,32 +12,34 @@ # See the License for the specific language governing permissions and # limitations under the License. +from playwright.sync_api import Page -def test_check_the_box(page): + +def test_check_the_box(page: Page) -> None: page.set_content('') page.check("input") assert page.evaluate("checkbox.checked") -def test_not_check_the_checked_box(page): +def test_not_check_the_checked_box(page: Page) -> None: page.set_content('') page.check("input") assert page.evaluate("checkbox.checked") -def test_uncheck_the_box(page): +def test_uncheck_the_box(page: Page) -> None: page.set_content('') page.uncheck("input") assert page.evaluate("checkbox.checked") is False -def test_not_uncheck_the_unchecked_box(page): +def test_not_uncheck_the_unchecked_box(page: Page) -> None: page.set_content('') page.uncheck("input") assert page.evaluate("checkbox.checked") is False -def test_check_the_box_by_label(page): +def test_check_the_box_by_label(page: Page) -> None: page.set_content( '' ) @@ -45,7 +47,7 @@ def test_check_the_box_by_label(page): assert page.evaluate("checkbox.checked") -def test_check_the_box_outside_label(page): +def test_check_the_box_outside_label(page: Page) -> None: page.set_content( '
' ) @@ -53,7 +55,7 @@ def test_check_the_box_outside_label(page): assert page.evaluate("checkbox.checked") -def test_check_the_box_inside_label_without_id(page): +def test_check_the_box_inside_label_without_id(page: Page) -> None: page.set_content( '' ) @@ -61,7 +63,7 @@ def test_check_the_box_inside_label_without_id(page): assert page.evaluate("checkbox.checked") -def test_check_radio(page): +def test_check_radio(page: Page) -> None: page.set_content( """ one @@ -72,7 +74,7 @@ def test_check_radio(page): assert page.evaluate("two.checked") -def test_check_the_box_by_aria_role(page): +def test_check_the_box_by_aria_role(page: Page) -> None: page.set_content( """