diff --git a/README.md b/README.md index bdf616079..901da2298 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 124.0.6367.29 | ✅ | ✅ | ✅ | +| Chromium 125.0.6422.26 | ✅ | ✅ | ✅ | | WebKit 17.4 | ✅ | ✅ | ✅ | -| Firefox 124.0 | ✅ | ✅ | ✅ | +| Firefox 125.0.1 | ✅ | ✅ | ✅ | ## Documentation diff --git a/ROLLING.md b/ROLLING.md index 5cd3240fa..2d35ee1e7 100644 --- a/ROLLING.md +++ b/ROLLING.md @@ -14,3 +14,10 @@ * generate API: `./scripts/update_api.sh` * commit changes & send PR * wait for bots to pass & merge the PR + + +## Fix typing issues with Playwright ToT + +1. `cd playwright` +1. `API_JSON_MODE=1 node utils/doclint/generateApiJson.js > ../playwright-python/playwright/driver/package/api.json` +1. `./scripts/update_api.sh` diff --git a/playwright/_impl/_assertions.py b/playwright/_impl/_assertions.py index 2c895e527..5841eca5a 100644 --- a/playwright/_impl/_assertions.py +++ b/playwright/_impl/_assertions.py @@ -16,7 +16,11 @@ from typing import Any, List, Optional, Pattern, Sequence, Union from urllib.parse import urljoin -from playwright._impl._api_structures import ExpectedTextValue, FrameExpectOptions +from playwright._impl._api_structures import ( + AriaRole, + ExpectedTextValue, + FrameExpectOptions, +) from playwright._impl._connection import format_call_log from playwright._impl._errors import Error from playwright._impl._fetch import APIResponse @@ -92,10 +96,10 @@ def _not(self) -> "PageAssertions": async def to_have_title( self, titleOrRegExp: Union[Pattern[str], str], timeout: float = None ) -> None: + __tracebackhide__ = True expected_values = to_expected_text_values( [titleOrRegExp], normalize_white_space=True ) - __tracebackhide__ = True await self._expect_impl( "to.have.title", FrameExpectOptions(expectedText=expected_values, timeout=timeout), @@ -110,13 +114,16 @@ async def not_to_have_title( await self._not.to_have_title(titleOrRegExp, timeout) async def to_have_url( - self, urlOrRegExp: Union[str, Pattern[str]], timeout: float = None + self, + urlOrRegExp: Union[str, Pattern[str]], + timeout: float = None, + ignoreCase: bool = None, ) -> None: __tracebackhide__ = True base_url = self._actual_page.context._options.get("baseURL") if isinstance(urlOrRegExp, str) and base_url: urlOrRegExp = urljoin(base_url, urlOrRegExp) - expected_text = to_expected_text_values([urlOrRegExp]) + expected_text = to_expected_text_values([urlOrRegExp], ignoreCase=ignoreCase) await self._expect_impl( "to.have.url", FrameExpectOptions(expectedText=expected_text, timeout=timeout), @@ -125,10 +132,13 @@ async def to_have_url( ) async def not_to_have_url( - self, urlOrRegExp: Union[Pattern[str], str], timeout: float = None + self, + urlOrRegExp: Union[Pattern[str], str], + timeout: float = None, + ignoreCase: bool = None, ) -> None: __tracebackhide__ = True - await self._not.to_have_url(urlOrRegExp, timeout) + await self._not.to_have_url(urlOrRegExp, timeout, ignoreCase) class LocatorAssertions(AssertionsBase): @@ -704,6 +714,70 @@ async def not_to_be_in_viewport( __tracebackhide__ = True await self._not.to_be_in_viewport(ratio=ratio, timeout=timeout) + async def to_have_accessible_description( + self, + description: Union[str, Pattern[str]], + ignoreCase: bool = None, + timeout: float = None, + ) -> None: + __tracebackhide__ = True + expected_values = to_expected_text_values([description], ignoreCase=ignoreCase) + await self._expect_impl( + "to.have.accessible.description", + FrameExpectOptions(expectedText=expected_values, timeout=timeout), + None, + "Locator expected to have accessible description", + ) + + async def not_to_have_accessible_description( + self, + name: Union[str, Pattern[str]], + ignoreCase: bool = None, + timeout: float = None, + ) -> None: + __tracebackhide__ = True + await self._not.to_have_accessible_description(name, ignoreCase, timeout) + + async def to_have_accessible_name( + self, + name: Union[str, Pattern[str]], + ignoreCase: bool = None, + timeout: float = None, + ) -> None: + __tracebackhide__ = True + expected_values = to_expected_text_values([name], ignoreCase=ignoreCase) + await self._expect_impl( + "to.have.accessible.name", + FrameExpectOptions(expectedText=expected_values, timeout=timeout), + None, + "Locator expected to have accessible name", + ) + + async def not_to_have_accessible_name( + self, + name: Union[str, Pattern[str]], + ignoreCase: bool = None, + timeout: float = None, + ) -> None: + __tracebackhide__ = True + await self._not.to_have_accessible_name(name, ignoreCase, timeout) + + async def to_have_role(self, role: AriaRole, timeout: float = None) -> None: + __tracebackhide__ = True + if isinstance(role, Pattern): + raise Error('"role" argument in to_have_role must be a string') + expected_values = to_expected_text_values([role]) + await self._expect_impl( + "to.have.role", + FrameExpectOptions(expectedText=expected_values, timeout=timeout), + None, + "Locator expected to have accessible role", + ) + + async def not_to_have_role(self, role: AriaRole, timeout: float = None) -> None: + __tracebackhide__ = True + await self._not.to_have_role(role, timeout) + class APIResponseAssertions: def __init__( diff --git a/playwright/_impl/_browser_context.py b/playwright/_impl/_browser_context.py index c540ce4c0..edb298c9c 100644 --- a/playwright/_impl/_browser_context.py +++ b/playwright/_impl/_browser_context.py @@ -510,6 +510,7 @@ def _on_close(self) -> None: self._browser._contexts.remove(self) self._dispose_har_routers() + self._tracing._reset_stack_counter() self.emit(BrowserContext.Events.Close, self) async def close(self, reason: str = None) -> None: diff --git a/playwright/_impl/_browser_type.py b/playwright/_impl/_browser_type.py index 8393d69ee..00e146061 100644 --- a/playwright/_impl/_browser_type.py +++ b/playwright/_impl/_browser_type.py @@ -218,6 +218,20 @@ async def connect( local_utils=self._connection.local_utils, ) connection.mark_as_remote() + + browser = None + + def handle_transport_close(reason: Optional[str]) -> None: + if browser: + for context in browser.contexts: + for page in context.pages: + page._on_close() + context._on_close() + browser._on_close() + connection.cleanup(reason) + + transport.once("close", handle_transport_close) + connection._is_sync = self._connection._is_sync connection._loop.create_task(connection.run()) playwright_future = connection.playwright_future @@ -240,16 +254,6 @@ async def connect( self._did_launch_browser(browser) browser._should_close_connection_on_close = True - def handle_transport_close() -> None: - for context in browser.contexts: - for page in context.pages: - page._on_close() - context._on_close() - browser._on_close() - connection.cleanup() - - transport.once("close", handle_transport_close) - return browser def _did_create_context( diff --git a/playwright/_impl/_connection.py b/playwright/_impl/_connection.py index b1cb245d8..eb4d182d3 100644 --- a/playwright/_impl/_connection.py +++ b/playwright/_impl/_connection.py @@ -284,10 +284,8 @@ async def stop_async(self) -> None: await self._transport.wait_until_stopped() self.cleanup() - def cleanup(self, cause: Exception = None) -> None: - self._closed_error = ( - TargetClosedError(str(cause)) if cause else TargetClosedError() - ) + def cleanup(self, cause: str = None) -> None: + self._closed_error = TargetClosedError(cause) if cause else TargetClosedError() if self._init_task and not self._init_task.done(): self._init_task.cancel() for ws_connection in self._child_ws_connections: @@ -305,7 +303,7 @@ def call_on_object_with_known_name( ) -> None: self._waiting_for_object[guid] = callback - def set_in_tracing(self, is_tracing: bool) -> None: + def set_is_tracing(self, is_tracing: bool) -> None: if is_tracing: self._tracing_count += 1 else: diff --git a/playwright/_impl/_fetch.py b/playwright/_impl/_fetch.py index 53c457ba7..9947534aa 100644 --- a/playwright/_impl/_fetch.py +++ b/playwright/_impl/_fetch.py @@ -96,6 +96,7 @@ def __init__( async def dispose(self) -> None: await self._channel.send("dispose") + self._tracing._reset_stack_counter() async def delete( self, diff --git a/playwright/_impl/_helper.py b/playwright/_impl/_helper.py index a79d9fe6a..fca945643 100644 --- a/playwright/_impl/_helper.py +++ b/playwright/_impl/_helper.py @@ -57,7 +57,7 @@ ForcedColors = Literal["active", "none", "null"] ReducedMotion = Literal["no-preference", "null", "reduce"] DocumentLoadState = Literal["commit", "domcontentloaded", "load", "networkidle"] -KeyboardModifier = Literal["Alt", "Control", "Meta", "Shift"] +KeyboardModifier = Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"] MouseButton = Literal["left", "middle", "right"] ServiceWorkersPolicy = Literal["allow", "block"] HarMode = Literal["full", "minimal"] diff --git a/playwright/_impl/_json_pipe.py b/playwright/_impl/_json_pipe.py index 12d3a886f..f76bc7175 100644 --- a/playwright/_impl/_json_pipe.py +++ b/playwright/_impl/_json_pipe.py @@ -13,11 +13,12 @@ # limitations under the License. import asyncio -from typing import Dict, cast +from typing import Dict, Optional, cast from pyee.asyncio import AsyncIOEventEmitter from playwright._impl._connection import Channel +from playwright._impl._errors import TargetClosedError from playwright._impl._helper import Error, ParsedMessagePayload from playwright._impl._transport import Transport @@ -53,8 +54,10 @@ def handle_message(message: Dict) -> None: return self.on_message(cast(ParsedMessagePayload, message)) - def handle_closed() -> None: - self.emit("close") + def handle_closed(reason: Optional[str]) -> None: + self.emit("close", reason) + if reason: + self.on_error_future.set_exception(TargetClosedError(reason)) self._stopped_future.set_result(None) self._pipe_channel.on( @@ -63,7 +66,7 @@ def handle_closed() -> None: ) self._pipe_channel.on( "closed", - lambda _: handle_closed(), + lambda params: handle_closed(params.get("reason")), ) async def run(self) -> None: diff --git a/playwright/_impl/_locator.py b/playwright/_impl/_locator.py index c5e92d874..0213ff9ea 100644 --- a/playwright/_impl/_locator.py +++ b/playwright/_impl/_locator.py @@ -116,6 +116,9 @@ async def _with_element( finally: await handle.dispose() + def _equals(self, locator: "Locator") -> bool: + return self._frame == locator._frame and self._selector == locator._selector + @property def page(self) -> "Page": return self._frame.page diff --git a/playwright/_impl/_page.py b/playwright/_impl/_page.py index 8efbaf164..43a9e06db 100644 --- a/playwright/_impl/_page.py +++ b/playwright/_impl/_page.py @@ -98,6 +98,25 @@ from playwright._impl._network import WebSocket +class LocatorHandler: + locator: "Locator" + handler: Union[Callable[["Locator"], Any], Callable[..., Any]] + times: Union[int, None] + + def __init__( + self, locator: "Locator", handler: Callable[..., Any], times: Union[int, None] + ) -> None: + self.locator = locator + self._handler = handler + self.times = times + + def __call__(self) -> Any: + arg_count = len(inspect.signature(self._handler).parameters) + if arg_count == 0: + return self._handler() + return self._handler(self.locator) + + class Page(ChannelOwner): Events = SimpleNamespace( Close="close", @@ -152,7 +171,7 @@ def __init__( self._close_reason: Optional[str] = None self._close_was_called = False self._har_routers: List[HarRouter] = [] - self._locator_handlers: Dict[str, Callable] = {} + self._locator_handlers: Dict[str, LocatorHandler] = {} self._channel.on( "bindingCall", @@ -1270,48 +1289,72 @@ async def set_checked( trial=trial, ) - async def add_locator_handler(self, locator: "Locator", handler: Callable) -> None: + async def add_locator_handler( + self, + locator: "Locator", + handler: Union[Callable[["Locator"], Any], Callable[[], Any]], + noWaitAfter: bool = None, + times: int = None, + ) -> None: if locator._frame != self._main_frame: raise Error("Locator must belong to the main frame of this page") + if times == 0: + return uid = await self._channel.send( "registerLocatorHandler", { "selector": locator._selector, + "noWaitAfter": noWaitAfter, }, ) - self._locator_handlers[uid] = handler + self._locator_handlers[uid] = LocatorHandler( + handler=handler, times=times, locator=locator + ) async def _on_locator_handler_triggered(self, uid: str) -> None: + remove = False try: - if self._dispatcher_fiber: - handler_finished_future = self._loop.create_future() - - def _handler() -> None: - try: - self._locator_handlers[uid]() - handler_finished_future.set_result(None) - except Exception as e: - handler_finished_future.set_exception(e) - - g = LocatorHandlerGreenlet(_handler) - g.switch() - await handler_finished_future - else: - coro_or_future = self._locator_handlers[uid]() - if coro_or_future: - await coro_or_future - + handler = self._locator_handlers.get(uid) + if handler and handler.times != 0: + if handler.times is not None: + handler.times -= 1 + if self._dispatcher_fiber: + handler_finished_future = self._loop.create_future() + + def _handler() -> None: + try: + handler() + handler_finished_future.set_result(None) + except Exception as e: + handler_finished_future.set_exception(e) + + g = LocatorHandlerGreenlet(_handler) + g.switch() + await handler_finished_future + else: + coro_or_future = handler() + if coro_or_future: + await coro_or_future + remove = handler.times == 0 finally: + if remove: + del self._locator_handlers[uid] try: await self._connection.wrap_api_call( lambda: self._channel.send( - "resolveLocatorHandlerNoReply", {"uid": uid} + "resolveLocatorHandlerNoReply", {"uid": uid, "remove": remove} ), is_internal=True, ) except Error: pass + async def remove_locator_handler(self, locator: "Locator") -> None: + for uid, data in self._locator_handlers.copy().items(): + if data.locator._equals(locator): + del self._locator_handlers[uid] + self._channel.send_no_reply("unregisterLocatorHandler", {"uid": uid}) + class Worker(ChannelOwner): Events = SimpleNamespace(Close="close") diff --git a/playwright/_impl/_tracing.py b/playwright/_impl/_tracing.py index 7f7972372..b2d4b5df9 100644 --- a/playwright/_impl/_tracing.py +++ b/playwright/_impl/_tracing.py @@ -58,7 +58,7 @@ async def start_chunk(self, title: str = None, name: str = None) -> None: async def _start_collecting_stacks(self, trace_name: str) -> None: if not self._is_tracing: self._is_tracing = True - self._connection.set_in_tracing(True) + self._connection.set_is_tracing(True) self._stacks_id = await self._connection.local_utils.tracing_started( self._traces_dir, trace_name ) @@ -74,9 +74,7 @@ async def _inner() -> None: await self._connection.wrap_api_call(_inner, True) async def _do_stop_chunk(self, file_path: Union[pathlib.Path, str] = None) -> None: - if self._is_tracing: - self._is_tracing = False - self._connection.set_in_tracing(False) + self._reset_stack_counter() if not file_path: # Not interested in any artifacts @@ -133,3 +131,8 @@ async def _do_stop_chunk(self, file_path: Union[pathlib.Path, str] = None) -> No "includeSources": self._include_sources, } ) + + def _reset_stack_counter(self) -> None: + if self._is_tracing: + self._is_tracing = False + self._connection.set_is_tracing(False) diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 244a891e3..696637c83 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -1140,7 +1140,8 @@ async def down(self, key: str) -> None: `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -1246,7 +1247,8 @@ async def press(self, key: str, *, delay: typing.Optional[float] = None) -> None `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -1846,7 +1848,7 @@ async def hover( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -1869,9 +1871,10 @@ async def hover( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -1904,7 +1907,7 @@ async def click( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -1930,9 +1933,10 @@ async def click( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -1974,7 +1978,7 @@ async def dblclick( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -2002,9 +2006,10 @@ async def dblclick( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -2120,7 +2125,7 @@ async def tap( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -2145,9 +2150,10 @@ async def tap( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -2377,7 +2383,8 @@ async def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -3321,6 +3328,9 @@ async def wait_for_load_state( committed when this method is called. If current document has already reached the required state, resolves immediately. + **NOTE** Most of the time, this method is not needed because Playwright + [auto-waits before every action](https://playwright.dev/python/docs/actionability). + **Usage** ```py @@ -4119,7 +4129,7 @@ async def click( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -4149,9 +4159,10 @@ async def click( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -4199,7 +4210,7 @@ async def dblclick( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -4231,9 +4242,10 @@ async def dblclick( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -4278,7 +4290,7 @@ async def tap( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -4307,9 +4319,10 @@ async def tap( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -5122,7 +5135,7 @@ async def hover( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -5149,9 +5162,10 @@ async def hover( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -5494,7 +5508,8 @@ async def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -7114,7 +7129,9 @@ def on( The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py async with page.expect_event(\"popup\") as page_info: @@ -7382,7 +7399,9 @@ def once( The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py async with page.expect_event(\"popup\") as page_info: @@ -8695,6 +8714,9 @@ async def wait_for_load_state( committed when this method is called. If current document has already reached the required state, resolves immediately. + **NOTE** Most of the time, this method is not needed because Playwright + [auto-waits before every action](https://playwright.dev/python/docs/actionability). + **Usage** ```py @@ -9065,6 +9087,9 @@ async def route( [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting `Browser.newContext.serviceWorkers` to `'block'`. + **NOTE** `page.route()` will not intercept the first request of a popup page. Use + `browser_context.route()` instead. + **Usage** An example of a naive handler that aborts all image requests: @@ -9381,7 +9406,7 @@ async def click( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -9411,9 +9436,10 @@ async def click( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -9461,7 +9487,7 @@ async def dblclick( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -9493,9 +9519,10 @@ async def dblclick( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -9540,7 +9567,7 @@ async def tap( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -9569,9 +9596,10 @@ async def tap( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -10382,7 +10410,7 @@ async def hover( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -10409,9 +10437,10 @@ async def hover( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -10773,7 +10802,8 @@ async def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -11691,12 +11721,17 @@ async def set_checked( ) async def add_locator_handler( - self, locator: "Locator", handler: typing.Callable + self, + locator: "Locator", + handler: typing.Union[ + typing.Callable[["Locator"], typing.Any], typing.Callable[[], typing.Any] + ], + *, + no_wait_after: typing.Optional[bool] = None, + times: typing.Optional[int] = None ) -> None: """Page.add_locator_handler - **NOTE** This method is experimental and its behavior may change in the upcoming releases. - When testing a web page, sometimes unexpected overlays like a \"Sign up\" dialog appear and block actions you want to automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making them tricky to handle in automated tests. @@ -11712,6 +11747,8 @@ async def add_locator_handler( is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't perform any actions, the handler will not be triggered. + - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible + anymore. You can opt-out of this behavior with `noWaitAfter`. - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your handler takes too long, it might cause timeouts. - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the @@ -11756,34 +11793,68 @@ def handler(): ``` An example with a custom callback on every actionability check. It uses a `
` locator that is always visible, - so the handler is called before every actionability check: + so the handler is called before every actionability check. It is important to specify `noWaitAfter`, because the + handler does not hide the `` element. ```py # Setup the handler. def handler(): page.evaluate(\"window.removeObstructionsForTestIfNeeded()\") - page.add_locator_handler(page.locator(\"body\"), handler) + page.add_locator_handler(page.locator(\"body\"), handler, no_wait_after=True) # Write the test as usual. page.goto(\"https://example.com\") page.get_by_role(\"button\", name=\"Start here\").click() ``` + Handler takes the original locator as an argument. You can also automatically remove the handler after a number of + invocations by setting `times`: + + ```py + def handler(locator): + locator.click() + page.add_locator_handler(page.get_by_label(\"Close\"), handler, times=1) + ``` + Parameters ---------- locator : Locator Locator that triggers the handler. - handler : Callable + handler : Union[Callable[[Locator], Any], Callable[[], Any]] Function that should be run once `locator` appears. This function should get rid of the element that blocks actions like click. + no_wait_after : Union[bool, None] + By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then + Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of + this behavior, so that overlay can stay visible after the handler has run. + times : Union[int, None] + Specifies the maximum number of times this handler should be called. Unlimited by default. """ return mapping.from_maybe_impl( await self._impl_obj.add_locator_handler( - locator=locator._impl_obj, handler=self._wrap_handler(handler) + locator=locator._impl_obj, + handler=self._wrap_handler(handler), + noWaitAfter=no_wait_after, + times=times, ) ) + async def remove_locator_handler(self, locator: "Locator") -> None: + """Page.remove_locator_handler + + Removes all locator handlers added by `page.add_locator_handler()` for a specific locator. + + Parameters + ---------- + locator : Locator + Locator passed to `page.add_locator_handler()`. + """ + + return mapping.from_maybe_impl( + await self._impl_obj.remove_locator_handler(locator=locator._impl_obj) + ) + mapping.register(PageImpl, Page) @@ -11907,7 +11978,9 @@ def on( The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py async with context.expect_page() as page_info: @@ -12085,7 +12158,9 @@ def once( The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py async with context.expect_page() as page_info: @@ -14734,7 +14809,7 @@ async def click( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -14780,9 +14855,10 @@ async def click( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -14824,7 +14900,7 @@ async def dblclick( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -14856,9 +14932,10 @@ async def dblclick( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -16085,7 +16162,7 @@ async def hover( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -16118,9 +16195,10 @@ async def hover( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -16419,7 +16497,8 @@ async def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -16762,7 +16841,7 @@ async def tap( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -16791,9 +16870,10 @@ async def tap( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -17345,9 +17425,7 @@ async def delete( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17423,9 +17501,7 @@ async def head( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17513,9 +17589,7 @@ async def get( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17591,9 +17665,7 @@ async def patch( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17669,9 +17741,7 @@ async def put( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17744,11 +17814,11 @@ async def post( files): The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` - encoding. You can achieve that with Playwright API like this: + encoding. Use `FormData` to construct request body and pass it to the request as `multipart` parameter: ```python api_request_context.post( - \"https://example.com/api/uploadScrip'\", + \"https://example.com/api/uploadScript'\", multipart={ \"fileField\": { \"name\": \"f.js\", @@ -17778,9 +17848,7 @@ async def post( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17834,11 +17902,12 @@ async def fetch( """APIRequestContext.fetch Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and - update context cookies from the response. The method will automatically follow redirects. JSON objects can be - passed directly to the request. + update context cookies from the response. The method will automatically follow redirects. **Usage** + JSON objects can be passed directly to the request: + ```python data = { \"title\": \"Book Title\", @@ -17847,8 +17916,8 @@ async def fetch( api_request_context.fetch(\"https://example.com/api/createBook\", method=\"post\", data=data) ``` - The common way to send file(s) in the body of a request is to encode it as form fields with `multipart/form-data` - encoding. You can achieve that with Playwright API like this: + The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` + encoding. Use `FormData` to construct request body and pass it to the request as `multipart` parameter: Parameters ---------- @@ -17873,9 +17942,7 @@ async def fetch( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -18070,7 +18137,8 @@ async def to_have_url( self, url_or_reg_exp: typing.Union[str, typing.Pattern[str]], *, - timeout: typing.Optional[float] = None + timeout: typing.Optional[float] = None, + ignore_case: typing.Optional[bool] = None ) -> None: """PageAssertions.to_have_url @@ -18092,12 +18160,15 @@ async def to_have_url( Expected URL string or RegExp. timeout : Union[float, None] Time to retry the assertion for in milliseconds. Defaults to `5000`. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. """ __tracebackhide__ = True return mapping.from_maybe_impl( await self._impl_obj.to_have_url( - urlOrRegExp=url_or_reg_exp, timeout=timeout + urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case ) ) @@ -18105,7 +18176,8 @@ async def not_to_have_url( self, url_or_reg_exp: typing.Union[typing.Pattern[str], str], *, - timeout: typing.Optional[float] = None + timeout: typing.Optional[float] = None, + ignore_case: typing.Optional[bool] = None ) -> None: """PageAssertions.not_to_have_url @@ -18117,12 +18189,15 @@ async def not_to_have_url( Expected URL string or RegExp. timeout : Union[float, None] Time to retry the assertion for in milliseconds. Defaults to `5000`. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. """ __tracebackhide__ = True return mapping.from_maybe_impl( await self._impl_obj.not_to_have_url( - urlOrRegExp=url_or_reg_exp, timeout=timeout + urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case ) ) @@ -19420,6 +19495,360 @@ async def not_to_be_in_viewport( await self._impl_obj.not_to_be_in_viewport(ratio=ratio, timeout=timeout) ) + async def to_have_accessible_description( + self, + description: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.to_have_accessible_description + + Ensures the `Locator` points to an element with a given + [accessible description](https://w3c.github.io/accname/#dfn-accessible-description). + + **Usage** + + ```py + locator = page.get_by_test_id(\"save-button\") + await expect(locator).to_have_accessible_description(\"Save results to disk\") + ``` + + Parameters + ---------- + description : Union[Pattern[str], str] + Expected accessible description. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + await self._impl_obj.to_have_accessible_description( + description=description, ignoreCase=ignore_case, timeout=timeout + ) + ) + + async def not_to_have_accessible_description( + self, + name: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.not_to_have_accessible_description + + The opposite of `locator_assertions.to_have_accessible_description()`. + + Parameters + ---------- + name : Union[Pattern[str], str] + Expected accessible description. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + await self._impl_obj.not_to_have_accessible_description( + name=name, ignoreCase=ignore_case, timeout=timeout + ) + ) + + async def to_have_accessible_name( + self, + name: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.to_have_accessible_name + + Ensures the `Locator` points to an element with a given + [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). + + **Usage** + + ```py + locator = page.get_by_test_id(\"save-button\") + await expect(locator).to_have_accessible_name(\"Save to disk\") + ``` + + Parameters + ---------- + name : Union[Pattern[str], str] + Expected accessible name. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + await self._impl_obj.to_have_accessible_name( + name=name, ignoreCase=ignore_case, timeout=timeout + ) + ) + + async def not_to_have_accessible_name( + self, + name: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.not_to_have_accessible_name + + The opposite of `locator_assertions.to_have_accessible_name()`. + + Parameters + ---------- + name : Union[Pattern[str], str] + Expected accessible name. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + await self._impl_obj.not_to_have_accessible_name( + name=name, ignoreCase=ignore_case, timeout=timeout + ) + ) + + async def to_have_role( + self, + role: Literal[ + "alert", + "alertdialog", + "application", + "article", + "banner", + "blockquote", + "button", + "caption", + "cell", + "checkbox", + "code", + "columnheader", + "combobox", + "complementary", + "contentinfo", + "definition", + "deletion", + "dialog", + "directory", + "document", + "emphasis", + "feed", + "figure", + "form", + "generic", + "grid", + "gridcell", + "group", + "heading", + "img", + "insertion", + "link", + "list", + "listbox", + "listitem", + "log", + "main", + "marquee", + "math", + "menu", + "menubar", + "menuitem", + "menuitemcheckbox", + "menuitemradio", + "meter", + "navigation", + "none", + "note", + "option", + "paragraph", + "presentation", + "progressbar", + "radio", + "radiogroup", + "region", + "row", + "rowgroup", + "rowheader", + "scrollbar", + "search", + "searchbox", + "separator", + "slider", + "spinbutton", + "status", + "strong", + "subscript", + "superscript", + "switch", + "tab", + "table", + "tablist", + "tabpanel", + "term", + "textbox", + "time", + "timer", + "toolbar", + "tooltip", + "tree", + "treegrid", + "treeitem", + ], + *, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.to_have_role + + Ensures the `Locator` points to an element with a given [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles). + + Note that role is matched as a string, disregarding the ARIA role hierarchy. For example, asserting a superclass + role `\"checkbox\"` on an element with a subclass role `\"switch\"` will fail. + + **Usage** + + ```py + locator = page.get_by_test_id(\"save-button\") + await expect(locator).to_have_role(\"button\") + ``` + + Parameters + ---------- + role : Union["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "directory", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] + Required aria role. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + await self._impl_obj.to_have_role(role=role, timeout=timeout) + ) + + async def not_to_have_role( + self, + role: Literal[ + "alert", + "alertdialog", + "application", + "article", + "banner", + "blockquote", + "button", + "caption", + "cell", + "checkbox", + "code", + "columnheader", + "combobox", + "complementary", + "contentinfo", + "definition", + "deletion", + "dialog", + "directory", + "document", + "emphasis", + "feed", + "figure", + "form", + "generic", + "grid", + "gridcell", + "group", + "heading", + "img", + "insertion", + "link", + "list", + "listbox", + "listitem", + "log", + "main", + "marquee", + "math", + "menu", + "menubar", + "menuitem", + "menuitemcheckbox", + "menuitemradio", + "meter", + "navigation", + "none", + "note", + "option", + "paragraph", + "presentation", + "progressbar", + "radio", + "radiogroup", + "region", + "row", + "rowgroup", + "rowheader", + "scrollbar", + "search", + "searchbox", + "separator", + "slider", + "spinbutton", + "status", + "strong", + "subscript", + "superscript", + "switch", + "tab", + "table", + "tablist", + "tabpanel", + "term", + "textbox", + "time", + "timer", + "toolbar", + "tooltip", + "tree", + "treegrid", + "treeitem", + ], + *, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.not_to_have_role + + The opposite of `locator_assertions.to_have_role()`. + + Parameters + ---------- + role : Union["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "directory", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] + Required aria role. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + await self._impl_obj.not_to_have_role(role=role, timeout=timeout) + ) + mapping.register(LocatorAssertionsImpl, LocatorAssertions) diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 6c1fe5fbb..b5076c9be 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -1136,7 +1136,8 @@ def down(self, key: str) -> None: `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -1244,7 +1245,8 @@ def press(self, key: str, *, delay: typing.Optional[float] = None) -> None: `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -1854,7 +1856,7 @@ def hover( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -1877,9 +1879,10 @@ def hover( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -1914,7 +1917,7 @@ def click( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -1940,9 +1943,10 @@ def click( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -1986,7 +1990,7 @@ def dblclick( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -2014,9 +2018,10 @@ def dblclick( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -2136,7 +2141,7 @@ def tap( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -2161,9 +2166,10 @@ def tap( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -2403,7 +2409,8 @@ def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -3375,6 +3382,9 @@ def wait_for_load_state( committed when this method is called. If current document has already reached the required state, resolves immediately. + **NOTE** Most of the time, this method is not needed because Playwright + [auto-waits before every action](https://playwright.dev/python/docs/actionability). + **Usage** ```py @@ -4198,7 +4208,7 @@ def click( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -4228,9 +4238,10 @@ def click( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -4280,7 +4291,7 @@ def dblclick( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -4312,9 +4323,10 @@ def dblclick( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -4361,7 +4373,7 @@ def tap( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -4390,9 +4402,10 @@ def tap( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -5217,7 +5230,7 @@ def hover( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -5244,9 +5257,10 @@ def hover( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -5601,7 +5615,8 @@ def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -7184,7 +7199,9 @@ def on(self, event: Literal["popup"], f: typing.Callable[["Page"], "None"]) -> N The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py with page.expect_event(\"popup\") as page_info: @@ -7404,7 +7421,9 @@ def once( The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py with page.expect_event(\"popup\") as page_info: @@ -8726,6 +8745,9 @@ def wait_for_load_state( committed when this method is called. If current document has already reached the required state, resolves immediately. + **NOTE** Most of the time, this method is not needed because Playwright + [auto-waits before every action](https://playwright.dev/python/docs/actionability). + **Usage** ```py @@ -9103,6 +9125,9 @@ def route( [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting `Browser.newContext.serviceWorkers` to `'block'`. + **NOTE** `page.route()` will not intercept the first request of a popup page. Use + `browser_context.route()` instead. + **Usage** An example of a naive handler that aborts all image requests: @@ -9429,7 +9454,7 @@ def click( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -9459,9 +9484,10 @@ def click( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -9511,7 +9537,7 @@ def dblclick( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -9543,9 +9569,10 @@ def dblclick( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -9592,7 +9619,7 @@ def tap( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -9621,9 +9648,10 @@ def tap( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -10446,7 +10474,7 @@ def hover( selector: str, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -10473,9 +10501,10 @@ def hover( selector : str A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -10849,7 +10878,8 @@ def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -11775,11 +11805,18 @@ def set_checked( ) ) - def add_locator_handler(self, locator: "Locator", handler: typing.Callable) -> None: + def add_locator_handler( + self, + locator: "Locator", + handler: typing.Union[ + typing.Callable[["Locator"], typing.Any], typing.Callable[[], typing.Any] + ], + *, + no_wait_after: typing.Optional[bool] = None, + times: typing.Optional[int] = None + ) -> None: """Page.add_locator_handler - **NOTE** This method is experimental and its behavior may change in the upcoming releases. - When testing a web page, sometimes unexpected overlays like a \"Sign up\" dialog appear and block actions you want to automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making them tricky to handle in automated tests. @@ -11795,6 +11832,8 @@ def add_locator_handler(self, locator: "Locator", handler: typing.Callable) -> N is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't perform any actions, the handler will not be triggered. + - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible + anymore. You can opt-out of this behavior with `noWaitAfter`. - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your handler takes too long, it might cause timeouts. - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the @@ -11839,36 +11878,70 @@ def handler(): ``` An example with a custom callback on every actionability check. It uses a `` locator that is always visible, - so the handler is called before every actionability check: + so the handler is called before every actionability check. It is important to specify `noWaitAfter`, because the + handler does not hide the `` element. ```py # Setup the handler. def handler(): await page.evaluate(\"window.removeObstructionsForTestIfNeeded()\") - await page.add_locator_handler(page.locator(\"body\"), handler) + await page.add_locator_handler(page.locator(\"body\"), handler, no_wait_after=True) # Write the test as usual. await page.goto(\"https://example.com\") await page.get_by_role(\"button\", name=\"Start here\").click() ``` + Handler takes the original locator as an argument. You can also automatically remove the handler after a number of + invocations by setting `times`: + + ```py + def handler(locator): + await locator.click() + await page.add_locator_handler(page.get_by_label(\"Close\"), handler, times=1) + ``` + Parameters ---------- locator : Locator Locator that triggers the handler. - handler : Callable + handler : Union[Callable[[Locator], Any], Callable[[], Any]] Function that should be run once `locator` appears. This function should get rid of the element that blocks actions like click. + no_wait_after : Union[bool, None] + By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then + Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of + this behavior, so that overlay can stay visible after the handler has run. + times : Union[int, None] + Specifies the maximum number of times this handler should be called. Unlimited by default. """ return mapping.from_maybe_impl( self._sync( self._impl_obj.add_locator_handler( - locator=locator._impl_obj, handler=self._wrap_handler(handler) + locator=locator._impl_obj, + handler=self._wrap_handler(handler), + noWaitAfter=no_wait_after, + times=times, ) ) ) + def remove_locator_handler(self, locator: "Locator") -> None: + """Page.remove_locator_handler + + Removes all locator handlers added by `page.add_locator_handler()` for a specific locator. + + Parameters + ---------- + locator : Locator + Locator passed to `page.add_locator_handler()`. + """ + + return mapping.from_maybe_impl( + self._sync(self._impl_obj.remove_locator_handler(locator=locator._impl_obj)) + ) + mapping.register(PageImpl, Page) @@ -11974,7 +12047,9 @@ def on(self, event: Literal["page"], f: typing.Callable[["Page"], "None"]) -> No The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py with context.expect_page() as page_info: @@ -12120,7 +12195,9 @@ def once( The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a popup with `window.open('http://example.com')`, this event will fire when the network request to - \"http://example.com\" is done and its response has started loading in the popup. + \"http://example.com\" is done and its response has started loading in the popup. If you would like to route/listen + to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively + instead of similar methods on the `Page`. ```py with context.expect_page() as page_info: @@ -14775,7 +14852,7 @@ def click( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -14821,9 +14898,10 @@ def click( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -14867,7 +14945,7 @@ def dblclick( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, delay: typing.Optional[float] = None, @@ -14899,9 +14977,10 @@ def dblclick( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -16147,7 +16226,7 @@ def hover( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -16180,9 +16259,10 @@ def hover( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -16495,7 +16575,8 @@ def press( `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. - Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the `key` in the upper case. @@ -16848,7 +16929,7 @@ def tap( self, *, modifiers: typing.Optional[ - typing.Sequence[Literal["Alt", "Control", "Meta", "Shift"]] + typing.Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] ] = None, position: typing.Optional[Position] = None, timeout: typing.Optional[float] = None, @@ -16877,9 +16958,10 @@ def tap( Parameters ---------- - modifiers : Union[Sequence[Union["Alt", "Control", "Meta", "Shift"]], None] + modifiers : Union[Sequence[Union["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]], None] Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores - current modifiers back. If not specified, currently pressed modifiers are used. + current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + "Control" on Windows and Linux and to "Meta" on macOS. position : Union[{x: float, y: float}, None] A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element. @@ -17441,9 +17523,7 @@ def delete( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17521,9 +17601,7 @@ def head( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17613,9 +17691,7 @@ def get( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17693,9 +17769,7 @@ def patch( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17773,9 +17847,7 @@ def put( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17850,11 +17922,11 @@ def post( ``` The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` - encoding. You can achieve that with Playwright API like this: + encoding. Use `FormData` to construct request body and pass it to the request as `multipart` parameter: ```python api_request_context.post( - \"https://example.com/api/uploadScrip'\", + \"https://example.com/api/uploadScript'\", multipart={ \"fileField\": { \"name\": \"f.js\", @@ -17884,9 +17956,7 @@ def post( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -17942,18 +18012,18 @@ def fetch( """APIRequestContext.fetch Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and - update context cookies from the response. The method will automatically follow redirects. JSON objects can be - passed directly to the request. + update context cookies from the response. The method will automatically follow redirects. **Usage** - The common way to send file(s) in the body of a request is to encode it as form fields with `multipart/form-data` - encoding. You can achieve that with Playwright API like this: + JSON objects can be passed directly to the request: + + The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` + encoding. Use `FormData` to construct request body and pass it to the request as `multipart` parameter: ```python api_request_context.fetch( - \"https://example.com/api/uploadScrip'\", - method=\"post\", + \"https://example.com/api/uploadScript\", method=\"post\", multipart={ \"fileField\": { \"name\": \"f.js\", @@ -17986,9 +18056,7 @@ def fetch( multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None] Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless - explicitly provided. File values can be passed either as - [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream) or as file-like object containing file - name, mime-type and its content. + explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content. timeout : Union[float, None] Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. fail_on_status_code : Union[bool, None] @@ -18191,7 +18259,8 @@ def to_have_url( self, url_or_reg_exp: typing.Union[str, typing.Pattern[str]], *, - timeout: typing.Optional[float] = None + timeout: typing.Optional[float] = None, + ignore_case: typing.Optional[bool] = None ) -> None: """PageAssertions.to_have_url @@ -18213,12 +18282,17 @@ def to_have_url( Expected URL string or RegExp. timeout : Union[float, None] Time to retry the assertion for in milliseconds. Defaults to `5000`. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. """ __tracebackhide__ = True return mapping.from_maybe_impl( self._sync( - self._impl_obj.to_have_url(urlOrRegExp=url_or_reg_exp, timeout=timeout) + self._impl_obj.to_have_url( + urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case + ) ) ) @@ -18226,7 +18300,8 @@ def not_to_have_url( self, url_or_reg_exp: typing.Union[typing.Pattern[str], str], *, - timeout: typing.Optional[float] = None + timeout: typing.Optional[float] = None, + ignore_case: typing.Optional[bool] = None ) -> None: """PageAssertions.not_to_have_url @@ -18238,13 +18313,16 @@ def not_to_have_url( Expected URL string or RegExp. timeout : Union[float, None] Time to retry the assertion for in milliseconds. Defaults to `5000`. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. """ __tracebackhide__ = True return mapping.from_maybe_impl( self._sync( self._impl_obj.not_to_have_url( - urlOrRegExp=url_or_reg_exp, timeout=timeout + urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case ) ) ) @@ -19577,6 +19655,368 @@ def not_to_be_in_viewport( ) ) + def to_have_accessible_description( + self, + description: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.to_have_accessible_description + + Ensures the `Locator` points to an element with a given + [accessible description](https://w3c.github.io/accname/#dfn-accessible-description). + + **Usage** + + ```py + locator = page.get_by_test_id(\"save-button\") + expect(locator).to_have_accessible_description(\"Save results to disk\") + ``` + + Parameters + ---------- + description : Union[Pattern[str], str] + Expected accessible description. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + self._sync( + self._impl_obj.to_have_accessible_description( + description=description, ignoreCase=ignore_case, timeout=timeout + ) + ) + ) + + def not_to_have_accessible_description( + self, + name: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.not_to_have_accessible_description + + The opposite of `locator_assertions.to_have_accessible_description()`. + + Parameters + ---------- + name : Union[Pattern[str], str] + Expected accessible description. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + self._sync( + self._impl_obj.not_to_have_accessible_description( + name=name, ignoreCase=ignore_case, timeout=timeout + ) + ) + ) + + def to_have_accessible_name( + self, + name: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.to_have_accessible_name + + Ensures the `Locator` points to an element with a given + [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). + + **Usage** + + ```py + locator = page.get_by_test_id(\"save-button\") + expect(locator).to_have_accessible_name(\"Save to disk\") + ``` + + Parameters + ---------- + name : Union[Pattern[str], str] + Expected accessible name. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + self._sync( + self._impl_obj.to_have_accessible_name( + name=name, ignoreCase=ignore_case, timeout=timeout + ) + ) + ) + + def not_to_have_accessible_name( + self, + name: typing.Union[str, typing.Pattern[str]], + *, + ignore_case: typing.Optional[bool] = None, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.not_to_have_accessible_name + + The opposite of `locator_assertions.to_have_accessible_name()`. + + Parameters + ---------- + name : Union[Pattern[str], str] + Expected accessible name. + ignore_case : Union[bool, None] + Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular + expression flag if specified. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + self._sync( + self._impl_obj.not_to_have_accessible_name( + name=name, ignoreCase=ignore_case, timeout=timeout + ) + ) + ) + + def to_have_role( + self, + role: Literal[ + "alert", + "alertdialog", + "application", + "article", + "banner", + "blockquote", + "button", + "caption", + "cell", + "checkbox", + "code", + "columnheader", + "combobox", + "complementary", + "contentinfo", + "definition", + "deletion", + "dialog", + "directory", + "document", + "emphasis", + "feed", + "figure", + "form", + "generic", + "grid", + "gridcell", + "group", + "heading", + "img", + "insertion", + "link", + "list", + "listbox", + "listitem", + "log", + "main", + "marquee", + "math", + "menu", + "menubar", + "menuitem", + "menuitemcheckbox", + "menuitemradio", + "meter", + "navigation", + "none", + "note", + "option", + "paragraph", + "presentation", + "progressbar", + "radio", + "radiogroup", + "region", + "row", + "rowgroup", + "rowheader", + "scrollbar", + "search", + "searchbox", + "separator", + "slider", + "spinbutton", + "status", + "strong", + "subscript", + "superscript", + "switch", + "tab", + "table", + "tablist", + "tabpanel", + "term", + "textbox", + "time", + "timer", + "toolbar", + "tooltip", + "tree", + "treegrid", + "treeitem", + ], + *, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.to_have_role + + Ensures the `Locator` points to an element with a given [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles). + + Note that role is matched as a string, disregarding the ARIA role hierarchy. For example, asserting a superclass + role `\"checkbox\"` on an element with a subclass role `\"switch\"` will fail. + + **Usage** + + ```py + locator = page.get_by_test_id(\"save-button\") + expect(locator).to_have_role(\"button\") + ``` + + Parameters + ---------- + role : Union["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "directory", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] + Required aria role. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + self._sync(self._impl_obj.to_have_role(role=role, timeout=timeout)) + ) + + def not_to_have_role( + self, + role: Literal[ + "alert", + "alertdialog", + "application", + "article", + "banner", + "blockquote", + "button", + "caption", + "cell", + "checkbox", + "code", + "columnheader", + "combobox", + "complementary", + "contentinfo", + "definition", + "deletion", + "dialog", + "directory", + "document", + "emphasis", + "feed", + "figure", + "form", + "generic", + "grid", + "gridcell", + "group", + "heading", + "img", + "insertion", + "link", + "list", + "listbox", + "listitem", + "log", + "main", + "marquee", + "math", + "menu", + "menubar", + "menuitem", + "menuitemcheckbox", + "menuitemradio", + "meter", + "navigation", + "none", + "note", + "option", + "paragraph", + "presentation", + "progressbar", + "radio", + "radiogroup", + "region", + "row", + "rowgroup", + "rowheader", + "scrollbar", + "search", + "searchbox", + "separator", + "slider", + "spinbutton", + "status", + "strong", + "subscript", + "superscript", + "switch", + "tab", + "table", + "tablist", + "tabpanel", + "term", + "textbox", + "time", + "timer", + "toolbar", + "tooltip", + "tree", + "treegrid", + "treeitem", + ], + *, + timeout: typing.Optional[float] = None + ) -> None: + """LocatorAssertions.not_to_have_role + + The opposite of `locator_assertions.to_have_role()`. + + Parameters + ---------- + role : Union["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "directory", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] + Required aria role. + timeout : Union[float, None] + Time to retry the assertion for in milliseconds. Defaults to `5000`. + """ + __tracebackhide__ = True + + return mapping.from_maybe_impl( + self._sync(self._impl_obj.not_to_have_role(role=role, timeout=timeout)) + ) + mapping.register(LocatorAssertionsImpl, LocatorAssertions) diff --git a/scripts/expected_api_mismatch.txt b/scripts/expected_api_mismatch.txt index 47c084c61..c101bba16 100644 --- a/scripts/expected_api_mismatch.txt +++ b/scripts/expected_api_mismatch.txt @@ -12,3 +12,6 @@ Parameter type mismatch in BrowserContext.route(handler=): documented as Callabl Parameter type mismatch in BrowserContext.unroute(handler=): documented as Union[Callable[[Route, Request], Union[Any, Any]], None], code has Union[Callable[[Route, Request], Any], Callable[[Route], Any], None] Parameter type mismatch in Page.route(handler=): documented as Callable[[Route, Request], Union[Any, Any]], code has Union[Callable[[Route, Request], Any], Callable[[Route], Any]] Parameter type mismatch in Page.unroute(handler=): documented as Union[Callable[[Route, Request], Union[Any, Any]], None], code has Union[Callable[[Route, Request], Any], Callable[[Route], Any], None] + +# One vs two arguments in the callback, Python explicitly unions. +Parameter type mismatch in Page.add_locator_handler(handler=): documented as Callable[[Locator], Any], code has Union[Callable[[Locator], Any], Callable[[], Any]] diff --git a/setup.py b/setup.py index 29cc21951..d65dc81a1 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.43.0" +driver_version = "1.44.0-beta-1715189091000" def extractall(zip: zipfile.ZipFile, path: str) -> None: diff --git a/tests/assets/input/handle-locator.html b/tests/assets/input/handle-locator.html index 865fb5364..f8f2111c9 100644 --- a/tests/assets/input/handle-locator.html +++ b/tests/assets/input/handle-locator.html @@ -50,9 +50,16 @@ }, false); close.addEventListener('click', () => { - interstitial.classList.remove('visible'); - target.classList.remove('hidden'); - target.classList.remove('removed'); + const closeInterstitial = () => { + interstitial.classList.remove('visible'); + target.classList.remove('hidden'); + target.classList.remove('removed'); + }; + + if (interstitial.classList.contains('timeout')) + setTimeout(closeInterstitial, 3000); + else + closeInterstitial(); }); let timesToShow = 0; @@ -65,9 +72,11 @@ if (!timesToShow && event !== 'none') target.removeEventListener(event, listener, capture === 'capture'); }; - if (event === 'hide') { + if (event === 'hide' || event === 'timeout') { target.classList.add('hidden'); listener(); + if (event === 'timeout') + interstitial.classList.add('timeout'); } else if (event === 'remove') { target.classList.add('removed'); listener(); diff --git a/tests/async/test_assertions.py b/tests/async/test_assertions.py index b8936f4bf..d61e625c7 100644 --- a/tests/async/test_assertions.py +++ b/tests/async/test_assertions.py @@ -84,6 +84,11 @@ async def test_assertions_page_to_have_url_with_base_url( await page.close() +async def test_assertions_page_to_have_url_support_ignore_case(page: Page) -> None: + await page.goto("data:text/html,