diff --git a/.claude/skills/playwright-roll/SKILL.md b/.claude/skills/playwright-roll/SKILL.md index 27dbc7553..89f19d7a4 100644 --- a/.claude/skills/playwright-roll/SKILL.md +++ b/.claude/skills/playwright-roll/SKILL.md @@ -174,9 +174,9 @@ Implement the change in `playwright/_impl/.py`. Use the upstream JS impl |---|---| | `async foo(): Promise` | `async def foo(self) -> X:` | | `foo(): X` (sync getter, no args, no body) | `@property def foo(self) -> X:` (the generator makes argument-less sync members properties — see `isPythonProperty` in `scripts/codegen/generate_api.js` — and reads `self._impl_obj.foo` without calling it. mypy does not catch a plain method here; the test does.) | -| `await this._channel.foo({ a, b })` | `await self._channel.send("foo", None, locals_to_params(locals()))` | -| `(await this._channel.foo()).value` | `await self._channel.send("foo", None)` (Python's `send()` auto-unwraps single-key responses; only call `send_return_as_dict` when the protocol returns multiple keys.) | -| `(await this._channel.foo()).artifact` (multi-key, may be empty) | `result = await self._channel.send_return_as_dict("foo", None); (result or {}).get("artifact")` — `send_return_as_dict` returns **`None`** (not `{}`) when the protocol response carries no fields. | +| `await this._channel.foo({ a, b }, this._timeout(options))` | `await self._channel.foo(a=a, b=b, timeout=self._timeout(timeout))`. The generated channel method takes every protocol parameter, with no defaults; an absent option is `None`. The implementation method's own parameters use the Python (snake_case) names, with no defaults. | +| `(await this._channel.foo()).value` | `(await self._channel.foo())["value"]`. The result dict has every top-level field; a field that the driver left out is `None`. | +| `this._channel.on('foo', ({ a }) => ...)` | `self._channel.onFoo(lambda event: ...)`; `event["a"]` is `None` when the driver left the optional field out. | | `try { ... } catch (e) { if (isTargetClosedError(e)) return; throw e; }` | `try: ...; except Exception as e: if is_target_closed_error(e): return; raise` (import from `playwright._impl._errors`) | | Inline `[Object]` return like `{endpoint: string}` | A `TypedDict` in `playwright/_impl/_api_structures.py`, which the generator writes. It names the struct from the docs: `` "Name" <[Object]> ``, or an `* alias-python:` line under it. See `RemoteAddr`, `BrowserBindResult`, `DebuggerPausedDetails`. | | `binary` event/return field | The Python channel layer hands you a base64 string. Decode with `base64.b64decode(value)` before exposing as `bytes`. See `Screencast._dispatch_frame`. | @@ -216,7 +216,7 @@ The generator fails with "the entry … no longer matches anything; remove it" w **Do not suppress** these — they're PORTs in disguise: - "Internal tooling" classes/methods whose `langs` field is empty (`Screencast.*`, `Debugger.*`, `Page.pick_locator`, `BrowserContext.debugger`, `Browser.bind/unbind`, `Page.{clear,console}_*`). The 1.59 roll suppressed all of these initially, then had to walk every one back. Verify `langs` first. -- A renamed positional parameter (mypy: unexpected keyword argument `newName` for the impl method). Just rename in `_impl/` and update the channel-send dict key. +- A renamed positional parameter (mypy: unexpected keyword argument `newName` for the impl method). Just rename in `_impl/`; if the wire name changed too, mypy reports the generated channel call. - A struct return the generator cannot name ("No Python name for the struct at …"). Name it, so that the generator writes a TypedDict — don't type it `Dict`. #### N/A @@ -241,7 +241,7 @@ The script, for `sync` and then `async`: 1. runs `node scripts/codegen/generate_api.js --flavor ` (reads the docs from `$PW_SRC_DIR`) straight into `playwright/_api/_generated.py`, 2. invokes `pre-commit run --files` on the generated file. -It then runs `python scripts/codegen/generate_protocol.py`, which reads `packages/protocol/spec/*.yml` from `$PW_SRC_DIR` and writes the command parameter tables to `playwright/_impl/_protocol.py`. +It then runs `python scripts/codegen/generate_protocol.py`, which reads `packages/protocol/spec/*.yml` from `$PW_SRC_DIR` and writes the command parameter tables to `playwright/_impl/_protocol.py`, the message types to `playwright/_impl/_protocol_types.py` and the typed channels to `playwright/_impl/_protocol_channels.py`. Then run `mypy playwright` and work through the errors in `_generated.py` — each one is a call into `_impl/` that the implementation does not support yet. @@ -261,7 +261,8 @@ Failure modes and fixes: | Symptom | Cause | Fix | |---|---|---| | mypy: `"XImpl" has no attribute "y"` in `_generated.py` | The docs have a new member `X.y`; `_impl/` does not. | PORT it. | -| mypy: `Unexpected keyword argument "z" for "y" of "XImpl"` | New parameter or option on an existing method. | Add the kwarg (docs camelCase name) in `_impl/`. If Python must not have it, fix the docs: `* langs:` on the option. | +| mypy: `Unexpected keyword argument "z" for "y" of "XImpl"` | New parameter or option on an existing method. | Add the parameter in `_impl/` under its Python (snake_case) name, as `Optional[X]` with no default, and pass it in the channel call. mypy then reports each caller that must pass it. If Python must not have it, fix the docs: `* langs:` on the option. | +| mypy: `Missing named argument "z" for "y" of "XChannel"` | The protocol added a parameter to a command. | Pass the value in the channel call. If the implementation method needs a new parameter for it, add it as above. | | mypy: incompatible argument type in `_generated.py` | The docs type changed, or `_impl/` declares a narrower type. | Match the type in `_impl/`, or fix the docs with a Python-only declaration (`* langs: python`). | | generator: `No Python name for the struct at X.y.z` | A new inline `[Object]` with no name in the docs. | Name it in the docs: `` "Name" <[Object]> ``. | | generator: `The struct X has different fields at A and B` | Two places in the docs share one struct name, but their fields differ. | Give one place its own name in the docs. | @@ -328,7 +329,7 @@ All keys use docs names: `Class.member` or `Class.member.arg`, with the upstream - **A cluster of rules on the same class is a smell.** If you're about to add five entries for `Foo.*`, you're almost certainly looking at a class that needs to be implemented. Implement the whole thing once and the entries disappear. - **Watch for revert pairs in the same range.** 1.59 added and reverted `Browser.isRemote` (#39613 / #39620) inside the same release. Walking chronologically lets you skip the add when you see the revert later. - **Watch for rename-revert pairs.** 1.59 had `Locator.normalize` → `Locator.toCode` (#39648) → `Locator.normalize` (#39754). Final state wins; only port the last. -- **Doc renames almost always include a wire-protocol rename.** Whenever you see `### param: X.y.oldName` → `### param: X.y.newName` in a doc commit, also `git -C "$PW_SRC_DIR" show -- packages/protocol/spec/` and the corresponding `*Dispatcher.ts` file. If the wire field changed too, the channel-send dict key in `_impl/` must change. Suppressing the doc-side mismatch is hiding a real bug — the previous Python code is silently sending an unknown field that the new server ignores. The test suite reports such a field as a protocol violation, but only in tests that send it. +- **Doc renames almost always include a wire-protocol rename.** Whenever you see `### param: X.y.oldName` → `### param: X.y.newName` in a doc commit, also `git -C "$PW_SRC_DIR" show -- packages/protocol/spec/` and the corresponding `*Dispatcher.ts` file. If the wire field changed too, the regenerated `_protocol_channels.py` renames the argument of the channel method, and mypy reports each call in `_impl/` that passes the old name. - **TypedDicts beat `Dict[str, X]` for any structured return.** When the docs describe a return as `[Object]` with named fields, name the struct for the generator, which writes its `TypedDict` into `_api_structures.py`, and re-export it from both public `__init__.py` files. Zero runtime cost (it's still a `dict`), and the docstrings expand its fields from the docs. - **Positional renames are free.** A param with no default before any `*` separator is positional-or-keyword in Python, but realistic call sites pass it positionally. Renaming such a param doesn't break callers. - **The "Backport changes" GitHub issue can be misleading.** In the 1.59 roll its checkboxes were all marked `[x]` with annotations like "✅ IMPLEMENTED", but several of those features had not actually been merged into the Python port. Trust the `docs/src/api/` walk over the issue. diff --git a/AGENTS.md b/AGENTS.md index df3537212..417089560 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,10 @@ Python bindings for [Playwright](https://playwright.dev). The Python client talk - `playwright/async_api/_generated.py`, `playwright/sync_api/_generated.py`, and the public TypedDicts in `playwright/_impl/_api_structures.py` — **auto-generated** from the upstream API docs. Never edit by hand; rerun `./scripts/update_api.sh` after a roll or after changing `scripts/codegen/`. - `scripts/codegen/generate_api.js` — the generator. A Node.js script that loads the upstream doc parser (`utils/doclint/api_parser.js`) from `$PW_SRC_DIR`, filters the docs for Python, and writes the whole public API: signatures, types, docstrings, event overloads, the calls into `_impl/`, and the TypedDicts. It does not read `_impl/`. - `scripts/codegen/python_rules.js` — the Python facts that the docs do not contain (positional options, struct `total=`, hidden members and options). Policy: the shipped Python API wins; each entry is a candidate for an upstream docs fix, and the generator fails on entries that no longer match anything. Every entry needs a one-line rationale comment above it. -- `playwright/_impl/_protocol.py` — **auto-generated** by `scripts/codegen/generate_protocol.py` (run by `./scripts/update_api.sh`) from the upstream protocol spec: the parameters of every command. With `PW_CHECK_PROTOCOL=1`, `playwright/_impl/_protocol_check.py` checks each message that the client sends against it. The test suite sets this, and fails a test that sends an unknown command, an unknown or missing parameter, or a value of the wrong type. +- `playwright/_impl/_protocol.py`, `_protocol_types.py` and `_protocol_channels.py` — **auto-generated** by `scripts/codegen/generate_protocol.py` (run by `./scripts/update_api.sh`) from the upstream protocol spec. + - `_protocol_channels.py` has a typed channel class for each interface (`PageChannel`, `FrameChannel`, …), like upstream's `channels.d.ts`. Each command is an async method with the protocol name and parameters (`await self._channel.goto(url=..., waitUntil=..., referer=..., timeout=...)`); each event has `on` / `off` methods. A result or event payload has every top-level field; a field that the driver left out is `None`. + - `_protocol_types.py` has the TypedDicts for initializers, results, events and nested types, named as upstream's `utils/generate_channels.js` names them (`PageInitializer`, `FrameGotoResult`, `PageRouteEvent`, …). + - `_protocol.py` has the parameters of every command. With `PW_CHECK_PROTOCOL=1`, `playwright/_impl/_protocol_check.py` checks each message that the client sends against it. The test suite sets this, and fails a test that sends an unknown command, an unknown or missing parameter, or a value of the wrong type. - `scripts/codegen/compare_api.py` — compares two generated files (signatures, annotations, call bodies, docstrings) and lists every difference. Use it to check that a generator change does not change the API. - `tests/async/`, `tests/sync/` — pytest suites, ported from the upstream tests. Most new tests are added to the async file with a sync mirror. See [Tests](#tests). - `DRIVER_VERSION` — the single source of truth for which Playwright release the driver is assembled from (one line, the `playwright-core` npm version, e.g. `1.61.0`, no `v` prefix). Read by `setup.py`, `scripts/build_driver.py`, and CI. The wheel build downloads `playwright-core` at this version from npm plus the matching Node.js binary and assembles the per-platform bundles — no source build. The version is baked into the staged bundle filenames (`driver/playwright--.zip`), so it doubles as the build cache key. @@ -80,7 +83,8 @@ It documents the full process: the upstream commit-range diff over `docs/src/api - Don't add `# type: ignore` or modify `_generated.py` to silence pyright; fix the source of the mismatch. - New public methods on impl classes need a sync test mirror under `tests/sync/`. - Keep `scripts/codegen/python_rules.js` minimal — every entry needs a one-line rationale comment above it. Prefer fixing the upstream docs. -- Prefer `locals_to_params(locals())` for forwarding optional kwargs to channel sends, matching the rest of the codebase. +- Implementation methods take the Python (snake_case) names that the generated API passes. Their options have no default: an option that can be absent is `Optional[X]` without `= None`. So mypy reports each caller that does not pass a new option. +- Send messages through the generated channel methods, never by name: `await self._channel.click(selector=selector, ..., clickCount=click_count, timeout=self._timeout(timeout))`. Pass every parameter, with the protocol (camelCase) name; an absent option is `None`, and the connection drops it. Declare the channel type on each ChannelOwner subclass (`_channel: "PageChannel"`). Listen to events with `self._channel.on(handler)`. ## Commit Convention diff --git a/playwright/_impl/_artifact.py b/playwright/_impl/_artifact.py index 9bfcec167..5f5f93c50 100644 --- a/playwright/_impl/_artifact.py +++ b/playwright/_impl/_artifact.py @@ -14,15 +14,20 @@ import pathlib from pathlib import Path -from typing import Dict, Optional, Union, cast +from typing import TYPE_CHECKING, Dict, Optional, Union, cast from playwright._impl._connection import ChannelOwner, from_channel from playwright._impl._errors import Error, patch_error_message from playwright._impl._helper import make_dirs_for_file from playwright._impl._stream import Stream +if TYPE_CHECKING: # pragma: no cover + from playwright._impl._protocol_channels import ArtifactChannel + class Artifact(ChannelOwner): + _channel: "ArtifactChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -34,55 +39,29 @@ async def path_after_finished(self) -> pathlib.Path: raise Error( "Path is not available when using browser_type.connect(). Use save_as() to save a local copy." ) - path = await self._channel.send( - "pathAfterFinished", - None, - ) + path = (await self._channel.pathAfterFinished())["value"] return pathlib.Path(path) async def save_as(self, path: Union[str, Path]) -> None: stream = cast( - Stream, - from_channel( - await self._channel.send( - "saveAsStream", - None, - ) - ), + Stream, from_channel((await self._channel.saveAsStream())["stream"]) ) make_dirs_for_file(path) await stream.save_as(path) async def failure(self) -> Optional[str]: - reason = await self._channel.send( - "failure", - None, - ) + reason = (await self._channel.failure())["error"] if reason is None: return None return patch_error_message(reason) async def delete(self) -> None: - await self._channel.send( - "delete", - None, - ) + await self._channel.delete() async def read_info_buffer(self) -> bytes: - stream = cast( - Stream, - from_channel( - await self._channel.send( - "stream", - None, - ) - ), - ) + stream = cast(Stream, from_channel((await self._channel.stream())["stream"])) buffer = await stream.read_all() return buffer async def cancel(self) -> None: # pyright: ignore[reportIncompatibleMethodOverride] - await self._channel.send( - "cancel", - None, - ) + await self._channel.cancel() diff --git a/playwright/_impl/_assertions.py b/playwright/_impl/_assertions.py index fdccf503f..41d944844 100644 --- a/playwright/_impl/_assertions.py +++ b/playwright/_impl/_assertions.py @@ -142,7 +142,10 @@ def __init__( message: Optional[str] = None, is_soft: bool = False, ) -> None: - super().__init__(page.locator(":root"), timeout, is_not, message, is_soft) + locator = page.locator( + selector=":root", has_text=None, has_not_text=None, has=None, has_not=None + ) + super().__init__(locator, timeout, is_not, message, is_soft) self._actual_page = page async def _call_expect( @@ -164,56 +167,62 @@ def _not(self) -> "PageAssertions": ) async def to_have_title( - self, titleOrRegExp: Union[Pattern[str], str], timeout: float = None + self, title_or_reg_exp: Union[Pattern[str], str], timeout: Optional[float] ) -> None: __tracebackhide__ = True expected_values = to_expected_text_values( - [titleOrRegExp], normalize_white_space=True + [title_or_reg_exp], normalize_white_space=True ) await self._expect_impl( "to.have.title", FrameExpectOptions(expectedText=expected_values, timeout=timeout), - titleOrRegExp, + title_or_reg_exp, "Page title expected to be", 'Expect "to_have_title"', ) async def not_to_have_title( - self, titleOrRegExp: Union[Pattern[str], str], timeout: float = None + self, title_or_reg_exp: Union[Pattern[str], str], timeout: Optional[float] ) -> None: __tracebackhide__ = True - await self._not.to_have_title(titleOrRegExp, timeout) + await self._not.to_have_title( + title_or_reg_exp=title_or_reg_exp, timeout=timeout + ) async def to_have_url( self, - urlOrRegExp: Union[str, Pattern[str]], - timeout: float = None, - ignoreCase: bool = None, + url_or_reg_exp: Union[str, Pattern[str]], + timeout: Optional[float], + ignore_case: Optional[bool], ) -> None: __tracebackhide__ = True base_url = self._actual_page.context._base_url - if isinstance(urlOrRegExp, str) and base_url: - urlOrRegExp = urljoin(base_url, urlOrRegExp) - expected_text = to_expected_text_values([urlOrRegExp], ignoreCase=ignoreCase) + if isinstance(url_or_reg_exp, str) and base_url: + url_or_reg_exp = urljoin(base_url, url_or_reg_exp) + expected_text = to_expected_text_values( + [url_or_reg_exp], ignore_case=ignore_case + ) await self._expect_impl( "to.have.url", FrameExpectOptions(expectedText=expected_text, timeout=timeout), - urlOrRegExp, + url_or_reg_exp, "Page URL expected to be", 'Expect "to_have_url"', ) async def not_to_have_url( self, - urlOrRegExp: Union[Pattern[str], str], - timeout: float = None, - ignoreCase: bool = None, + url_or_reg_exp: Union[Pattern[str], str], + timeout: Optional[float], + ignore_case: Optional[bool], ) -> None: __tracebackhide__ = True - await self._not.to_have_url(urlOrRegExp, timeout, ignoreCase) + await self._not.to_have_url( + url_or_reg_exp=url_or_reg_exp, timeout=timeout, ignore_case=ignore_case + ) async def to_match_aria_snapshot( - self, expected: str, timeout: float = None + self, expected: str, timeout: Optional[float] ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -225,10 +234,10 @@ async def to_match_aria_snapshot( ) async def not_to_match_aria_snapshot( - self, expected: str, timeout: float = None + self, expected: str, timeout: Optional[float] ) -> None: __tracebackhide__ = True - await self._not.to_match_aria_snapshot(expected, timeout) + await self._not.to_match_aria_snapshot(expected=expected, timeout=timeout) class LocatorAssertions(AssertionsBase): @@ -268,9 +277,9 @@ async def to_contain_text( Pattern[str], str, ], - useInnerText: bool = None, - timeout: float = None, - ignoreCase: bool = None, + use_inner_text: Optional[bool], + timeout: Optional[float], + ignore_case: Optional[bool], ) -> None: __tracebackhide__ = True if isinstance(expected, collections.abc.Sequence) and not isinstance( @@ -280,13 +289,13 @@ async def to_contain_text( expected, match_substring=True, normalize_white_space=True, - ignoreCase=ignoreCase, + ignore_case=ignore_case, ) await self._expect_impl( "to.contain.text.array", FrameExpectOptions( expectedText=expected_text, - useInnerText=useInnerText, + useInnerText=use_inner_text, timeout=timeout, ), expected, @@ -298,13 +307,13 @@ async def to_contain_text( [expected], match_substring=True, normalize_white_space=True, - ignoreCase=ignoreCase, + ignore_case=ignore_case, ) await self._expect_impl( "to.have.text", FrameExpectOptions( expectedText=expected_text, - useInnerText=useInnerText, + useInnerText=use_inner_text, timeout=timeout, ), expected, @@ -321,19 +330,24 @@ async def not_to_contain_text( Pattern[str], str, ], - useInnerText: bool = None, - timeout: float = None, - ignoreCase: bool = None, + use_inner_text: Optional[bool], + timeout: Optional[float], + ignore_case: Optional[bool], ) -> None: __tracebackhide__ = True - await self._not.to_contain_text(expected, useInnerText, timeout, ignoreCase) + await self._not.to_contain_text( + expected=expected, + use_inner_text=use_inner_text, + timeout=timeout, + ignore_case=ignore_case, + ) async def to_have_attribute( self, name: str, - value: Union[Pattern[str], str] = None, - ignoreCase: bool = None, - timeout: float = None, + value: Optional[Union[Pattern[str], str]], + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True if value is None: @@ -345,7 +359,7 @@ async def to_have_attribute( 'Expect "to_have_attribute"', ) return - expected_text = to_expected_text_values([value], ignoreCase=ignoreCase) + expected_text = to_expected_text_values([value], ignore_case=ignore_case) await self._expect_impl( "to.have.attribute.value", FrameExpectOptions( @@ -359,13 +373,13 @@ async def to_have_attribute( async def not_to_have_attribute( self, name: str, - value: Union[Pattern[str], str] = None, - ignoreCase: bool = None, - timeout: float = None, + value: Optional[Union[Pattern[str], str]], + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._not.to_have_attribute( - name, value, ignoreCase=ignoreCase, timeout=timeout + name=name, value=value, ignore_case=ignore_case, timeout=timeout ) async def to_have_class( @@ -377,7 +391,7 @@ async def to_have_class( Pattern[str], str, ], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True if isinstance(expected, collections.abc.Sequence) and not isinstance( @@ -410,10 +424,10 @@ async def not_to_have_class( Pattern[str], str, ], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_class(expected, timeout) + await self._not.to_have_class(expected=expected, timeout=timeout) async def to_contain_class( self, @@ -421,7 +435,7 @@ async def to_contain_class( Sequence[str], str, ], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True if isinstance(expected, collections.abc.Sequence) and not isinstance( @@ -451,15 +465,15 @@ async def not_to_contain_class( Sequence[str], str, ], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_contain_class(expected, timeout) + await self._not.to_contain_class(expected=expected, timeout=timeout) async def to_have_count( self, count: int, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -473,17 +487,17 @@ async def to_have_count( async def not_to_have_count( self, count: int, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_count(count, timeout) + await self._not.to_have_count(count=count, timeout=timeout) async def to_have_css( self, name: str, value: Union[str, Pattern[str]], - timeout: float = None, - pseudo: Literal["after", "before"] = None, + timeout: Optional[float], + pseudo: Optional[Literal["after", "before"]], ) -> None: __tracebackhide__ = True expected_text = to_expected_text_values([value]) @@ -504,16 +518,18 @@ async def not_to_have_css( self, name: str, value: Union[str, Pattern[str]], - timeout: float = None, - pseudo: Literal["after", "before"] = None, + timeout: Optional[float], + pseudo: Optional[Literal["after", "before"]], ) -> None: __tracebackhide__ = True - await self._not.to_have_css(name, value, timeout, pseudo) + await self._not.to_have_css( + name=name, value=value, timeout=timeout, pseudo=pseudo + ) async def to_have_id( self, id: Union[str, Pattern[str]], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True expected_text = to_expected_text_values([id]) @@ -528,16 +544,16 @@ async def to_have_id( async def not_to_have_id( self, id: Union[str, Pattern[str]], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_id(id, timeout) + await self._not.to_have_id(id=id, timeout=timeout) async def to_have_js_property( self, name: str, value: Any, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -554,15 +570,15 @@ async def not_to_have_js_property( self, name: str, value: Any, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_js_property(name, value, timeout) + await self._not.to_have_js_property(name=name, value=value, timeout=timeout) async def to_have_value( self, value: Union[str, Pattern[str]], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True expected_text = to_expected_text_values([value]) @@ -577,17 +593,17 @@ async def to_have_value( async def not_to_have_value( self, value: Union[str, Pattern[str]], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_value(value, timeout) + await self._not.to_have_value(value=value, timeout=timeout) async def to_have_values( self, values: Union[ Sequence[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]] ], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True expected_text = to_expected_text_values(values) @@ -604,10 +620,10 @@ async def not_to_have_values( values: Union[ Sequence[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]] ], - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_values(values, timeout) + await self._not.to_have_values(values=values, timeout=timeout) async def to_have_text( self, @@ -618,9 +634,9 @@ async def to_have_text( Pattern[str], str, ], - useInnerText: bool = None, - timeout: float = None, - ignoreCase: bool = None, + use_inner_text: Optional[bool], + timeout: Optional[float], + ignore_case: Optional[bool], ) -> None: __tracebackhide__ = True if isinstance(expected, collections.abc.Sequence) and not isinstance( @@ -629,13 +645,13 @@ async def to_have_text( expected_text = to_expected_text_values( expected, normalize_white_space=True, - ignoreCase=ignoreCase, + ignore_case=ignore_case, ) await self._expect_impl( "to.have.text.array", FrameExpectOptions( expectedText=expected_text, - useInnerText=useInnerText, + useInnerText=use_inner_text, timeout=timeout, ), expected, @@ -644,13 +660,13 @@ async def to_have_text( ) else: expected_text = to_expected_text_values( - [expected], normalize_white_space=True, ignoreCase=ignoreCase + [expected], normalize_white_space=True, ignore_case=ignore_case ) await self._expect_impl( "to.have.text", FrameExpectOptions( expectedText=expected_text, - useInnerText=useInnerText, + useInnerText=use_inner_text, timeout=timeout, ), expected, @@ -667,17 +683,22 @@ async def not_to_have_text( Pattern[str], str, ], - useInnerText: bool = None, - timeout: float = None, - ignoreCase: bool = None, + use_inner_text: Optional[bool], + timeout: Optional[float], + ignore_case: Optional[bool], ) -> None: __tracebackhide__ = True - await self._not.to_have_text(expected, useInnerText, timeout, ignoreCase) + await self._not.to_have_text( + expected=expected, + use_inner_text=use_inner_text, + timeout=timeout, + ignore_case=ignore_case, + ) async def to_be_attached( self, - attached: bool = None, - timeout: float = None, + attached: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True if attached is None: @@ -693,9 +714,9 @@ async def to_be_attached( async def to_be_checked( self, - timeout: float = None, - checked: bool = None, - indeterminate: bool = None, + timeout: Optional[float], + checked: Optional[bool], + indeterminate: Optional[bool], ) -> None: __tracebackhide__ = True expected_value = {} @@ -718,22 +739,22 @@ async def to_be_checked( async def not_to_be_attached( self, - attached: bool = None, - timeout: float = None, + attached: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._not.to_be_attached(attached=attached, timeout=timeout) async def not_to_be_checked( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_checked(timeout) + await self._not.to_be_checked(timeout=timeout, checked=None, indeterminate=None) async def to_be_disabled( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -746,15 +767,15 @@ async def to_be_disabled( async def not_to_be_disabled( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_disabled(timeout) + await self._not.to_be_disabled(timeout=timeout) async def to_be_editable( self, - editable: bool = None, - timeout: float = None, + editable: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True if editable is None: @@ -770,15 +791,15 @@ async def to_be_editable( async def not_to_be_editable( self, - editable: bool = None, - timeout: float = None, + editable: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_editable(editable, timeout) + await self._not.to_be_editable(editable=editable, timeout=timeout) async def to_be_empty( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -791,15 +812,15 @@ async def to_be_empty( async def not_to_be_empty( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_empty(timeout) + await self._not.to_be_empty(timeout=timeout) async def to_be_enabled( self, - enabled: bool = None, - timeout: float = None, + enabled: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True if enabled is None: @@ -815,15 +836,15 @@ async def to_be_enabled( async def not_to_be_enabled( self, - enabled: bool = None, - timeout: float = None, + enabled: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_enabled(enabled, timeout) + await self._not.to_be_enabled(enabled=enabled, timeout=timeout) async def to_be_hidden( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -836,15 +857,15 @@ async def to_be_hidden( async def not_to_be_hidden( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_hidden(timeout) + await self._not.to_be_hidden(timeout=timeout) async def to_be_visible( self, - visible: bool = None, - timeout: float = None, + visible: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True if visible is None: @@ -860,15 +881,15 @@ async def to_be_visible( async def not_to_be_visible( self, - visible: bool = None, - timeout: float = None, + visible: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_visible(visible, timeout) + await self._not.to_be_visible(visible=visible, timeout=timeout) async def to_be_focused( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -881,15 +902,15 @@ async def to_be_focused( async def not_to_be_focused( self, - timeout: float = None, + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_be_focused(timeout) + await self._not.to_be_focused(timeout=timeout) async def to_be_in_viewport( self, - ratio: float = None, - timeout: float = None, + ratio: Optional[float], + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -901,7 +922,7 @@ async def to_be_in_viewport( ) async def not_to_be_in_viewport( - self, ratio: float = None, timeout: float = None + self, ratio: Optional[float], timeout: Optional[float] ) -> None: __tracebackhide__ = True await self._not.to_be_in_viewport(ratio=ratio, timeout=timeout) @@ -909,12 +930,12 @@ async def not_to_be_in_viewport( async def to_have_accessible_description( self, description: Union[str, Pattern[str]], - ignoreCase: bool = None, - timeout: float = None, + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True expected_values = to_expected_text_values( - [description], ignoreCase=ignoreCase, normalize_white_space=True + [description], ignore_case=ignore_case, normalize_white_space=True ) await self._expect_impl( "to.have.accessible.description", @@ -927,21 +948,23 @@ async def to_have_accessible_description( async def not_to_have_accessible_description( self, description: Union[str, Pattern[str]], - ignoreCase: bool = None, - timeout: float = None, + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_accessible_description(description, ignoreCase, timeout) + await self._not.to_have_accessible_description( + description=description, ignore_case=ignore_case, timeout=timeout + ) async def to_have_accessible_name( self, name: Union[str, Pattern[str]], - ignoreCase: bool = None, - timeout: float = None, + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True expected_values = to_expected_text_values( - [name], ignoreCase=ignoreCase, normalize_white_space=True + [name], ignore_case=ignore_case, normalize_white_space=True ) await self._expect_impl( "to.have.accessible.name", @@ -954,13 +977,15 @@ async def to_have_accessible_name( async def not_to_have_accessible_name( self, name: Union[str, Pattern[str]], - ignoreCase: bool = None, - timeout: float = None, + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True - await self._not.to_have_accessible_name(name, ignoreCase, timeout) + await self._not.to_have_accessible_name( + name=name, ignore_case=ignore_case, timeout=timeout + ) - async def to_have_role(self, role: AriaRole, timeout: float = None) -> None: + async def to_have_role(self, role: AriaRole, timeout: Optional[float]) -> None: __tracebackhide__ = True if isinstance(role, Pattern): raise Error('"role" argument in to_have_role must be a string') @@ -975,13 +1000,13 @@ async def to_have_role(self, role: AriaRole, timeout: float = None) -> None: async def to_have_accessible_error_message( self, - errorMessage: Union[str, Pattern[str]], - ignoreCase: bool = None, - timeout: float = None, + error_message: Union[str, Pattern[str]], + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True expected_values = to_expected_text_values( - [errorMessage], ignoreCase=ignoreCase, normalize_white_space=True + [error_message], ignore_case=ignore_case, normalize_white_space=True ) await self._expect_impl( "to.have.accessible.error.message", @@ -993,21 +1018,21 @@ async def to_have_accessible_error_message( async def not_to_have_accessible_error_message( self, - errorMessage: Union[str, Pattern[str]], - ignoreCase: bool = None, - timeout: float = None, + error_message: Union[str, Pattern[str]], + ignore_case: Optional[bool], + timeout: Optional[float], ) -> None: __tracebackhide__ = True await self._not.to_have_accessible_error_message( - errorMessage=errorMessage, ignoreCase=ignoreCase, timeout=timeout + error_message=error_message, ignore_case=ignore_case, timeout=timeout ) - async def not_to_have_role(self, role: AriaRole, timeout: float = None) -> None: + async def not_to_have_role(self, role: AriaRole, timeout: Optional[float]) -> None: __tracebackhide__ = True - await self._not.to_have_role(role, timeout) + await self._not.to_have_role(role=role, timeout=timeout) async def to_match_aria_snapshot( - self, expected: str, timeout: float = None + self, expected: str, timeout: Optional[float] ) -> None: __tracebackhide__ = True await self._expect_impl( @@ -1019,10 +1044,10 @@ async def to_match_aria_snapshot( ) async def not_to_match_aria_snapshot( - self, expected: str, timeout: float = None + self, expected: str, timeout: Optional[float] ) -> None: __tracebackhide__ = True - await self._not.to_match_aria_snapshot(expected, timeout) + await self._not.to_match_aria_snapshot(expected=expected, timeout=timeout) class APIResponseAssertions: @@ -1081,17 +1106,16 @@ def expected_regex( pattern: Pattern[str], match_substring: bool, normalize_white_space: bool, - ignoreCase: Optional[bool] = None, + ignore_case: Optional[bool] = None, ) -> ExpectedTextValue: expected = ExpectedTextValue( regexSource=pattern.pattern, regexFlags=escape_regex_flags(pattern), matchSubstring=match_substring, normalizeWhiteSpace=normalize_white_space, - ignoreCase=ignoreCase, ) - if expected["ignoreCase"] is None: - del expected["ignoreCase"] + if ignore_case is not None: + expected["ignoreCase"] = ignore_case return expected @@ -1101,7 +1125,7 @@ def to_expected_text_values( ], match_substring: bool = False, normalize_white_space: bool = False, - ignoreCase: Optional[bool] = None, + ignore_case: Optional[bool] = None, ) -> Sequence[ExpectedTextValue]: out: List[ExpectedTextValue] = [] if not isinstance(items, (list, tuple)): @@ -1112,14 +1136,15 @@ def to_expected_text_values( string=item, matchSubstring=match_substring, normalizeWhiteSpace=normalize_white_space, - ignoreCase=ignoreCase, ) - if o["ignoreCase"] is None: - del o["ignoreCase"] + if ignore_case is not None: + o["ignoreCase"] = ignore_case out.append(o) elif isinstance(item, Pattern): out.append( - expected_regex(item, match_substring, normalize_white_space, ignoreCase) + expected_regex( + item, match_substring, normalize_white_space, ignore_case + ) ) else: raise Error("value must be a string or regular expression") diff --git a/playwright/_impl/_browser.py b/playwright/_impl/_browser.py index 605e637e0..d4c7dbc54 100644 --- a/playwright/_impl/_browser.py +++ b/playwright/_impl/_browser.py @@ -36,10 +36,16 @@ ViewportSize, ) from playwright._impl._artifact import Artifact -from playwright._impl._browser_context import BrowserContext +from playwright._impl._browser_context import ( + BrowserContext, + to_accept_downloads_params, + to_record_video_params, + to_storage_state_params, +) from playwright._impl._cdp_session import CDPSession from playwright._impl._connection import ChannelOwner, from_channel from playwright._impl._errors import is_target_closed_error +from playwright._impl._headers import serialize_headers from playwright._impl._helper import ( ColorScheme, Contrast, @@ -48,16 +54,23 @@ HarMode, ReducedMotion, ServiceWorkersPolicy, - locals_to_params, make_dirs_for_file, ) +from playwright._impl._network import ( + to_client_certificates_protocol, + to_http_credentials_protocol, +) from playwright._impl._page import Page if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._browser_type import BrowserType + from playwright._impl._protocol_channels import BrowserChannel class Browser(ChannelOwner): + _channel: "BrowserChannel" + Events = SimpleNamespace( Context="context", Disconnected="disconnected", @@ -74,13 +87,12 @@ def __init__( self._contexts: Set[BrowserContext] = set() self._traces_dir: Optional[str] = None - self._channel.on( - "context", - lambda params: self._did_create_context( - cast(BrowserContext, from_channel(params["context"])) - ), + self._channel.onContext( + lambda event: self._did_create_context( + cast(BrowserContext, from_channel(event["context"])) + ) ) - self._channel.on("close", lambda _: self._on_close()) + self._channel.onClose(lambda: self._on_close()) self._close_reason: Optional[str] = None def __repr__(self) -> str: @@ -131,103 +143,185 @@ def is_connected(self) -> bool: async def new_context( self, - viewport: ViewportSize = None, - screen: ViewportSize = None, - noViewport: bool = None, - ignoreHTTPSErrors: bool = None, - javaScriptEnabled: bool = None, - bypassCSP: bool = None, - userAgent: str = None, - locale: str = None, - timezoneId: str = None, - geolocation: Geolocation = None, - permissions: Sequence[str] = None, - extraHTTPHeaders: Dict[str, str] = None, - offline: bool = None, - httpCredentials: Union[HttpCredentials, Sequence[HttpCredentials]] = None, - deviceScaleFactor: float = None, - isMobile: bool = None, - hasTouch: bool = None, - colorScheme: ColorScheme = None, - reducedMotion: ReducedMotion = None, - forcedColors: ForcedColors = None, - contrast: Contrast = None, - acceptDownloads: bool = None, - defaultBrowserType: str = None, - proxy: ProxySettings = None, - recordHarPath: Union[Path, str] = None, - recordHarOmitContent: bool = None, - recordVideoDir: Union[Path, str] = None, - recordVideoSize: ViewportSize = None, - storageState: Union[StorageState, str, Path] = None, - baseURL: str = None, - strictSelectors: bool = None, - serviceWorkers: ServiceWorkersPolicy = None, - recordHarUrlFilter: Union[Pattern[str], str] = None, - recordHarMode: HarMode = None, - recordHarContent: HarContentPolicy = None, - clientCertificates: List[ClientCertificate] = None, - recordVideoFps: int = None, + viewport: Optional[ViewportSize], + screen: Optional[ViewportSize], + no_viewport: Optional[bool], + ignore_https_errors: Optional[bool], + java_script_enabled: Optional[bool], + bypass_csp: Optional[bool], + user_agent: Optional[str], + locale: Optional[str], + timezone_id: Optional[str], + geolocation: Optional[Geolocation], + permissions: Optional[Sequence[str]], + extra_http_headers: Optional[Dict[str, str]], + offline: Optional[bool], + http_credentials: Optional[Union[HttpCredentials, Sequence[HttpCredentials]]], + device_scale_factor: Optional[float], + is_mobile: Optional[bool], + has_touch: Optional[bool], + color_scheme: Optional[ColorScheme], + reduced_motion: Optional[ReducedMotion], + forced_colors: Optional[ForcedColors], + contrast: Optional[Contrast], + accept_downloads: Optional[bool], + default_browser_type: Optional[str], # Not a protocol parameter; not sent. + proxy: Optional[ProxySettings], + record_har_path: Optional[Union[Path, str]], + record_har_omit_content: Optional[bool], + record_video_dir: Optional[Union[Path, str]], + record_video_size: Optional[ViewportSize], + storage_state: Optional[Union[StorageState, str, Path]], + base_url: Optional[str], + strict_selectors: Optional[bool], + service_workers: Optional[ServiceWorkersPolicy], + record_har_url_filter: Optional[Union[Pattern[str], str]], + record_har_mode: Optional[HarMode], + record_har_content: Optional[HarContentPolicy], + client_certificates: Optional[List[ClientCertificate]], + record_video_fps: Optional[int], ) -> BrowserContext: - params = locals_to_params(locals()) assert self._browser_type is not None - await self._browser_type._prepare_browser_context_params(params) - - channel = await self._channel.send("newContext", None, params) - context = cast(BrowserContext, from_channel(channel)) + selectors = self._browser_type._playwright.selectors + result = await self._channel.newContext( + noDefaultViewport=True if no_viewport else None, + viewport=viewport, + screen=screen, + ignoreHTTPSErrors=ignore_https_errors, + clientCertificates=cast( + "Optional[List[channels.BrowserNewContextParamsClientCertificates]]", + await to_client_certificates_protocol(client_certificates), + ), + javaScriptEnabled=java_script_enabled, + bypassCSP=bypass_csp, + userAgent=user_agent, + locale=locale, + timezoneId=timezone_id, + geolocation=cast( + "Optional[channels.BrowserNewContextParamsGeolocation]", geolocation + ), + permissions=permissions, + extraHTTPHeaders=( + serialize_headers(extra_http_headers) + if extra_http_headers is not None + else None + ), + offline=offline, + httpCredentials=cast( + "Optional[List[channels.HttpCredentials]]", + to_http_credentials_protocol(http_credentials), + ), + deviceScaleFactor=device_scale_factor, + isMobile=is_mobile, + hasTouch=has_touch, + colorScheme="no-override" if color_scheme == "null" else color_scheme, + reducedMotion=( + "no-override" if reduced_motion == "null" else reduced_motion + ), + forcedColors="no-override" if forced_colors == "null" else forced_colors, + acceptDownloads=to_accept_downloads_params(accept_downloads), + contrast="no-override" if contrast == "null" else contrast, + baseURL=base_url, + recordVideo=to_record_video_params( + record_video_dir, record_video_size, record_video_fps + ), + strictSelectors=strict_selectors, + serviceWorkers=service_workers, + selectorEngines=selectors._selector_engines, + testIdAttributeName=selectors._test_id_attribute_name, + proxy=cast("Optional[channels.BrowserNewContextParamsProxy]", proxy), + storageState=await to_storage_state_params(storage_state), + ) + context = cast(BrowserContext, from_channel(result["context"])) await context._initialize_har_from_options( - record_har_content=recordHarContent, - record_har_mode=recordHarMode, - record_har_omit_content=recordHarOmitContent, - record_har_path=recordHarPath, - record_har_url_filter=recordHarUrlFilter, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, ) return context async def new_page( self, - viewport: ViewportSize = None, - screen: ViewportSize = None, - noViewport: bool = None, - ignoreHTTPSErrors: bool = None, - javaScriptEnabled: bool = None, - bypassCSP: bool = None, - userAgent: str = None, - locale: str = None, - timezoneId: str = None, - geolocation: Geolocation = None, - permissions: Sequence[str] = None, - extraHTTPHeaders: Dict[str, str] = None, - offline: bool = None, - httpCredentials: Union[HttpCredentials, Sequence[HttpCredentials]] = None, - deviceScaleFactor: float = None, - isMobile: bool = None, - hasTouch: bool = None, - colorScheme: ColorScheme = None, - forcedColors: ForcedColors = None, - contrast: Contrast = None, - reducedMotion: ReducedMotion = None, - acceptDownloads: bool = None, - defaultBrowserType: str = None, - proxy: ProxySettings = None, - recordHarPath: Union[Path, str] = None, - recordHarOmitContent: bool = None, - recordVideoDir: Union[Path, str] = None, - recordVideoSize: ViewportSize = None, - storageState: Union[StorageState, str, Path] = None, - baseURL: str = None, - strictSelectors: bool = None, - serviceWorkers: ServiceWorkersPolicy = None, - recordHarUrlFilter: Union[Pattern[str], str] = None, - recordHarMode: HarMode = None, - recordHarContent: HarContentPolicy = None, - clientCertificates: List[ClientCertificate] = None, - recordVideoFps: int = None, + viewport: Optional[ViewportSize], + screen: Optional[ViewportSize], + no_viewport: Optional[bool], + ignore_https_errors: Optional[bool], + java_script_enabled: Optional[bool], + bypass_csp: Optional[bool], + user_agent: Optional[str], + locale: Optional[str], + timezone_id: Optional[str], + geolocation: Optional[Geolocation], + permissions: Optional[Sequence[str]], + extra_http_headers: Optional[Dict[str, str]], + offline: Optional[bool], + http_credentials: Optional[Union[HttpCredentials, Sequence[HttpCredentials]]], + device_scale_factor: Optional[float], + is_mobile: Optional[bool], + has_touch: Optional[bool], + color_scheme: Optional[ColorScheme], + forced_colors: Optional[ForcedColors], + contrast: Optional[Contrast], + reduced_motion: Optional[ReducedMotion], + accept_downloads: Optional[bool], + default_browser_type: Optional[str], + proxy: Optional[ProxySettings], + record_har_path: Optional[Union[Path, str]], + record_har_omit_content: Optional[bool], + record_video_dir: Optional[Union[Path, str]], + record_video_size: Optional[ViewportSize], + storage_state: Optional[Union[StorageState, str, Path]], + base_url: Optional[str], + strict_selectors: Optional[bool], + service_workers: Optional[ServiceWorkersPolicy], + record_har_url_filter: Optional[Union[Pattern[str], str]], + record_har_mode: Optional[HarMode], + record_har_content: Optional[HarContentPolicy], + client_certificates: Optional[List[ClientCertificate]], + record_video_fps: Optional[int], ) -> Page: - params = locals_to_params(locals()) - async def inner() -> Page: - context = await self.new_context(**params) + context = await self.new_context( + viewport=viewport, + screen=screen, + no_viewport=no_viewport, + ignore_https_errors=ignore_https_errors, + java_script_enabled=java_script_enabled, + bypass_csp=bypass_csp, + user_agent=user_agent, + locale=locale, + timezone_id=timezone_id, + geolocation=geolocation, + permissions=permissions, + extra_http_headers=extra_http_headers, + offline=offline, + http_credentials=http_credentials, + device_scale_factor=device_scale_factor, + is_mobile=is_mobile, + has_touch=has_touch, + color_scheme=color_scheme, + reduced_motion=reduced_motion, + forced_colors=forced_colors, + contrast=contrast, + accept_downloads=accept_downloads, + default_browser_type=default_browser_type, + proxy=proxy, + record_har_path=record_har_path, + record_har_omit_content=record_har_omit_content, + record_video_dir=record_video_dir, + record_video_size=record_video_size, + storage_state=storage_state, + base_url=base_url, + strict_selectors=strict_selectors, + service_workers=service_workers, + record_har_url_filter=record_har_url_filter, + record_har_mode=record_har_mode, + record_har_content=record_har_content, + client_certificates=client_certificates, + record_video_fps=record_video_fps, + ) page = await context.new_page() page._owned_context = context context._owner_page = page @@ -235,13 +329,13 @@ async def inner() -> Page: return await self._connection.wrap_api_call(inner, title="Create page") - async def close(self, reason: str = None) -> None: + async def close(self, reason: Optional[str]) -> None: self._close_reason = reason try: if self._should_close_connection_on_close: await self._connection.stop_async() else: - await self._channel.send("close", None, {"reason": reason}) + await self._channel.close(reason=reason) except Exception as e: if not is_target_closed_error(e): raise e @@ -251,41 +345,41 @@ def version(self) -> str: return self._initializer["version"] async def new_browser_cdp_session(self) -> CDPSession: - return from_channel(await self._channel.send("newBrowserCDPSession", None)) + return from_channel((await self._channel.newBrowserCDPSession())["session"]) async def bind( self, title: str, - workspaceDir: str = None, - host: str = None, - port: int = None, + workspace_dir: Optional[str], + host: Optional[str], + port: Optional[int], ) -> BrowserBindResult: - return await self._channel.send_return_as_dict( - "startServer", None, locals_to_params(locals()) + return await self._channel.startServer( + title=title, workspaceDir=workspace_dir, metadata=None, host=host, port=port ) async def unbind(self) -> None: - await self._channel.send("stopServer", None) + await self._channel.stopServer() async def start_tracing( self, - page: Page = None, - path: Union[str, Path] = None, - screenshots: bool = None, - categories: Sequence[str] = None, + page: Optional[Page], + path: Optional[Union[str, Path]], + screenshots: Optional[bool], + categories: Optional[Sequence[str]], ) -> None: - params = locals_to_params(locals()) # The client writes the trace to `path` in stop_tracing(). - params.pop("path", None) - if page: - params["page"] = page._channel if path: self._cr_tracing_path = str(path) - await self._channel.send("startTracing", None, params) + await self._channel.startTracing( + page=page._channel if page else None, + screenshots=screenshots, + categories=categories, + ) async def stop_tracing(self) -> bytes: artifact = cast( - Artifact, from_channel(await self._channel.send("stopTracing", None)) + Artifact, from_channel((await self._channel.stopTracing())["artifact"]) ) buffer = await artifact.read_info_buffer() await artifact.delete() diff --git a/playwright/_impl/_browser_context.py b/playwright/_impl/_browser_context.py index 73b4a092a..9ed964d5e 100644 --- a/playwright/_impl/_browser_context.py +++ b/playwright/_impl/_browser_context.py @@ -34,6 +34,7 @@ from playwright._impl._api_structures import ( Cookie, Geolocation, + RecordVideoSize, SetCookieParam, StorageState, WebErrorLocation, @@ -67,7 +68,6 @@ async_readfile, async_writefile, create_task_and_ignore_exception, - locals_to_params, to_impl, ) from playwright._impl._network import ( @@ -76,19 +76,23 @@ Route, WebSocketRoute, WebSocketRouteHandler, - to_clear_cookies_params, ) from playwright._impl._page import BindingCall, Page, Worker +from playwright._impl._str_utils import escape_regex_flags from playwright._impl._timeout_settings import TimeoutSettings from playwright._impl._tracing import Tracing from playwright._impl._waiter import Waiter from playwright._impl._web_error import WebError if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._browser import Browser + from playwright._impl._protocol_channels import BrowserContextChannel class BrowserContext(ChannelOwner): + _channel: "BrowserContextChannel" + Events = SimpleNamespace( # Deprecated in v1.56, never emitted anymore. BackgroundPage="backgroundpage", @@ -136,88 +140,70 @@ def __init__( self._request._timeout_settings = self._timeout_settings self._clock = Clock(self) self._credentials = Credentials(self) - self._channel.on( - "bindingCall", - lambda params: self._on_binding(from_channel(params["binding"])), - ) - self._channel.on("close", lambda _: self._on_close()) - self._channel.on( - "page", lambda params: self._on_page(from_channel(params["page"])) + self._channel.onBindingCall( + lambda event: self._on_binding(from_channel(event["binding"])) ) - self._channel.on( - "route", - lambda params: self._loop.create_task( + self._channel.onClose(lambda: self._on_close()) + self._channel.onPage(lambda event: self._on_page(from_channel(event["page"]))) + self._channel.onRoute( + lambda event: self._loop.create_task( self._on_route( - from_channel(params.get("route")), + from_channel(event["route"]), ) ), ) - self._channel.on( - "webSocketRoute", - lambda params: self._loop.create_task( + self._channel.onWebSocketRoute( + lambda event: self._loop.create_task( self._on_web_socket_route( - from_channel(params["webSocketRoute"]), + from_channel(event["webSocketRoute"]), ) ), ) - self._channel.on( - "serviceWorker", - lambda params: self._on_service_worker(from_channel(params["worker"])), - ) - self._channel.on( - "console", - lambda event: self._on_console_message(event), + self._channel.onServiceWorker( + lambda event: self._on_service_worker(from_channel(event["worker"])) ) + self._channel.onConsole(self._on_console_message) - self._channel.on( - "dialog", lambda params: self._on_dialog(from_channel(params["dialog"])) + self._channel.onDialog( + lambda event: self._on_dialog(from_channel(event["dialog"])) ) - self._channel.on( - "dialogClosed", - lambda params: self._on_dialog_closed(from_channel(params["dialog"])), + self._channel.onDialogClosed( + lambda event: self._on_dialog_closed(from_channel(event["dialog"])) ) - self._channel.on( - "pageError", - lambda params: self._on_page_error( - parse_error(params["error"]["error"]), - from_nullable_channel(params["page"]), - cast( - WebErrorLocation, - params.get("location") or {"url": "", "line": 0, "column": 0}, - ), + self._channel.onPageError( + lambda event: self._on_page_error( + parse_error(event["error"]["error"]), + from_nullable_channel(event["page"]), + event["location"], ), ) - self._channel.on( - "request", - lambda params: self._on_request( - from_channel(params["request"]), - from_nullable_channel(params.get("page")), + self._channel.onRequest( + lambda event: self._on_request( + from_channel(event["request"]), + from_nullable_channel(event["page"]), ), ) - self._channel.on( - "response", - lambda params: self._on_response( - from_channel(params["response"]), - from_nullable_channel(params.get("page")), + self._channel.onResponse( + lambda event: self._on_response( + from_channel(event["response"]), + from_nullable_channel(event["page"]), ), ) - self._channel.on( - "requestFailed", - lambda params: self._on_request_failed( - from_channel(params["request"]), - params["responseEndTiming"], - params.get("failureText"), - from_nullable_channel(params.get("page")), + self._channel.onRequestFailed( + lambda event: self._on_request_failed( + from_channel(event["request"]), + event["responseEndTiming"], + event["failureText"], + from_nullable_channel(event["page"]), ), ) - self._channel.on( - "requestFinished", - lambda params: self._on_request_finished( - from_channel(params["request"]), - from_nullable_channel(params.get("response")), - params["responseEndTiming"], - from_nullable_channel(params.get("page")), + self._channel.onRequestFinished( + lambda event: self._on_request_finished( + from_channel(event["request"]), + from_nullable_channel(event["response"]), + event["responseEndTiming"], + from_nullable_channel(event["page"]), ), ) self._closed_future: asyncio.Future = asyncio.Future() @@ -349,57 +335,76 @@ async def _initialize_har_from_options( async def new_page(self) -> Page: if self._owner_page: raise Error("Please use browser.new_context()") - return from_channel(await self._channel.send("newPage", None)) + return from_channel((await self._channel.newPage())["page"]) - async def cookies(self, urls: Union[str, Sequence[str]] = None) -> List[Cookie]: + async def cookies(self, urls: Optional[Union[str, Sequence[str]]]) -> List[Cookie]: if urls is None: urls = [] if isinstance(urls, str): urls = [urls] - return await self._channel.send("cookies", None, dict(urls=urls)) + result = await self._channel.cookies(urls=urls) + return cast(List[Cookie], result["cookies"]) async def add_cookies(self, cookies: Sequence[SetCookieParam]) -> None: - await self._channel.send("addCookies", None, dict(cookies=cookies)) + await self._channel.addCookies( + cookies=cast("Sequence[channels.SetNetworkCookie]", cookies) + ) async def clear_cookies( self, - name: Union[str, Pattern[str]] = None, - domain: Union[str, Pattern[str]] = None, - path: Union[str, Pattern[str]] = None, + name: Optional[Union[str, Pattern[str]]], + domain: Optional[Union[str, Pattern[str]]], + path: Optional[Union[str, Pattern[str]]], ) -> None: - await self._channel.send( - "clearCookies", None, to_clear_cookies_params(name, domain, path) + await self._channel.clearCookies( + name=name if isinstance(name, str) else None, + nameRegexSource=name.pattern if isinstance(name, Pattern) else None, + nameRegexFlags=( + escape_regex_flags(name) if isinstance(name, Pattern) else None + ), + domain=domain if isinstance(domain, str) else None, + domainRegexSource=domain.pattern if isinstance(domain, Pattern) else None, + domainRegexFlags=( + escape_regex_flags(domain) if isinstance(domain, Pattern) else None + ), + path=path if isinstance(path, str) else None, + pathRegexSource=path.pattern if isinstance(path, Pattern) else None, + pathRegexFlags=( + escape_regex_flags(path) if isinstance(path, Pattern) else None + ), ) async def grant_permissions( - self, permissions: Sequence[str], origin: str = None + self, permissions: Sequence[str], origin: Optional[str] ) -> None: - await self._channel.send("grantPermissions", None, locals_to_params(locals())) + await self._channel.grantPermissions(permissions=permissions, origin=origin) async def clear_permissions(self) -> None: - await self._channel.send("clearPermissions", None) + await self._channel.clearPermissions() - async def set_geolocation(self, geolocation: Geolocation = None) -> None: - await self._channel.send("setGeolocation", None, locals_to_params(locals())) + async def set_geolocation(self, geolocation: Optional[Geolocation]) -> None: + await self._channel.setGeolocation( + geolocation=cast( + "Optional[channels.BrowserContextSetGeolocationParamsGeolocation]", + geolocation, + ) + ) async def set_extra_http_headers(self, headers: Dict[str, str]) -> None: - await self._channel.send( - "setExtraHTTPHeaders", None, dict(headers=serialize_headers(headers)) - ) + await self._channel.setExtraHTTPHeaders(headers=serialize_headers(headers)) async def set_offline(self, offline: bool) -> None: - await self._channel.send("setOffline", None, dict(offline=offline)) + await self._channel.setOffline(offline=offline) async def add_init_script( - self, script: str = None, path: Union[str, Path] = None + self, script: Optional[str], path: Optional[Union[str, Path]] ) -> Disposable: if path: script = (await async_readfile(path)).decode() if not isinstance(script, str): raise Error("Either path or script parameter must be specified") - return from_channel( - await self._channel.send("addInitScript", None, dict(source=script)) - ) + result = await self._channel.addInitScript(source=script) + return from_channel(result["disposable"]) async def expose_binding(self, name: str, callback: Callable) -> Disposable: for page in self._pages: @@ -410,15 +415,16 @@ async def expose_binding(self, name: str, callback: Callable) -> Disposable: if name in self._bindings: raise Error(f'Function "{name}" has been already registered') self._bindings[name] = callback - return from_channel( - await self._channel.send("exposeBinding", None, dict(name=name)) - ) + result = await self._channel.exposeBinding(name=name, noGlobal=None) + return from_channel(result["disposable"]) async def expose_function(self, name: str, callback: Callable) -> Disposable: - return await self.expose_binding(name, lambda source, *args: callback(*args)) + return await self.expose_binding( + name=name, callback=lambda source, *args: callback(*args) + ) async def route( - self, url: URLMatch, handler: RouteHandlerCallback, times: int = None + self, url: URLMatch, handler: RouteHandlerCallback, times: Optional[int] ) -> DisposableStub: self._routes.insert( 0, @@ -431,10 +437,10 @@ async def route( ), ) await self._update_interception_patterns() - return DisposableStub(lambda: self.unroute(url, handler), self) + return DisposableStub(lambda: self.unroute(url=url, handler=handler), self) async def unroute( - self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None + self, url: URLMatch, handler: Optional[RouteHandlerCallback] ) -> None: removed = [] remaining = [] @@ -471,7 +477,7 @@ def _dispose_har_routers(self) -> None: self._har_routers = [] async def unroute_all( - self, behavior: Literal["default", "ignoreErrors", "wait"] = None + self, behavior: Optional[Literal["default", "ignoreErrors", "wait"]] ) -> None: await self._unroute_internal(self._routes, [], behavior) self._dispose_har_routers() @@ -479,49 +485,47 @@ async def unroute_all( async def route_from_har( self, har: Union[Path, str], - url: Union[Pattern[str], str] = None, - notFound: RouteFromHarNotFoundPolicy = None, - update: bool = None, - updateContent: Literal["attach", "embed"] = None, - updateMode: HarMode = None, + url: Optional[Union[Pattern[str], str]], + not_found: Optional[RouteFromHarNotFoundPolicy], + update: Optional[bool], + update_content: Optional[Literal["attach", "embed"]], + update_mode: Optional[HarMode], ) -> None: if update: await self._tracing._record_into_har( har=har, page=None, url=url, - update_content=updateContent, - update_mode=updateMode, + update_content=update_content, + update_mode=update_mode, ) return router = await HarRouter.create( local_utils=self._connection.local_utils, file=str(har), - not_found_action=notFound or "abort", + not_found_action=not_found or "abort", url_matcher=url, ) self._har_routers.append(router) await router.add_context_route(self) async def _update_interception_patterns(self) -> None: - patterns = RouteHandler.prepare_interception_patterns(self._routes) - await self._channel.send( - "setNetworkInterceptionPatterns", None, {"patterns": patterns} + await self._channel.setNetworkInterceptionPatterns( + patterns=RouteHandler.prepare_interception_patterns(self._routes) ) async def _update_web_socket_interception_patterns(self) -> None: - patterns = WebSocketRouteHandler.prepare_interception_patterns( - self._web_socket_routes - ) - await self._channel.send( - "setWebSocketInterceptionPatterns", None, {"patterns": patterns} + await self._channel.setWebSocketInterceptionPatterns( + patterns=WebSocketRouteHandler.prepare_interception_patterns( + self._web_socket_routes + ) ) def expect_event( self, event: str, - predicate: Callable = None, - timeout: float = None, + predicate: Optional[Callable], + timeout: Optional[float], ) -> EventContextManagerImpl: if timeout is None: timeout = self._timeout_settings.timeout() @@ -558,7 +562,7 @@ def _on_close(self) -> None: def is_closed(self) -> bool: return self._closing_or_closed - async def close(self, reason: str = None) -> None: + async def close(self, reason: Optional[str]) -> None: if self._closing_or_closed: return self._close_reason = reason @@ -570,33 +574,31 @@ async def _inner_close() -> None: await self._tracing._export_all_hars() await self._channel._connection.wrap_api_call(_inner_close, True) - await self._channel.send("close", None, {"reason": reason}) + await self._channel.close(reason=reason) await self._closed_future async def storage_state( self, - path: Union[str, Path] = None, - indexedDB: bool = None, - opfs: bool = None, - credentials: bool = None, + path: Optional[Union[str, Path]], + indexed_db: Optional[bool], + opfs: Optional[bool], + credentials: Optional[bool], ) -> StorageState: - result = await self._channel.send_return_as_dict( - "storageState", - None, - {"indexedDB": indexedDB, "opfs": opfs, "credentials": credentials}, + result = await self._channel.storageState( + indexedDB=indexed_db, opfs=opfs, credentials=credentials ) + # Leave out the fields that the driver did not send, such as "credentials". + state = {key: value for key, value in result.items() if value is not None} if path: - await async_writefile(path, json.dumps(result)) - return result + await async_writefile(path, json.dumps(state)) + return cast(StorageState, state) async def set_storage_state( - self, storageState: Union[StorageState, str, Path] + self, storage_state: Union[StorageState, str, Path] ) -> None: - if isinstance(storageState, (str, Path)): - state = json.loads(await async_readfile(storageState)) - else: - state = storageState - await self._channel.send("setStorageState", None, {"storageState": state}) + await self._channel.setStorageState( + storageState=await to_storage_state_params(storage_state) + ) def _effective_close_reason(self) -> Optional[str]: if self._close_reason: @@ -606,25 +608,31 @@ def _effective_close_reason(self) -> Optional[str]: return None async def wait_for_event( - self, event: str, predicate: Callable = None, timeout: float = None + self, event: str, predicate: Optional[Callable], timeout: Optional[float] ) -> Any: - async with self.expect_event(event, predicate, timeout) as event_info: + async with self.expect_event( + event=event, predicate=predicate, timeout=timeout + ) as event_info: pass return await event_info def expect_console_message( self, - predicate: Callable[[ConsoleMessage], bool] = None, - timeout: float = None, + predicate: Optional[Callable[[ConsoleMessage], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl[ConsoleMessage]: - return self.expect_event(Page.Events.Console, predicate, timeout) + return self.expect_event( + event=Page.Events.Console, predicate=predicate, timeout=timeout + ) def expect_page( self, - predicate: Callable[[Page], bool] = None, - timeout: float = None, + predicate: Optional[Callable[[Page], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl[Page]: - return self.expect_event(BrowserContext.Events.Page, predicate, timeout) + return self.expect_event( + event=BrowserContext.Events.Page, predicate=predicate, timeout=timeout + ) def _on_service_worker(self, worker: Worker) -> None: worker._context = self @@ -658,7 +666,7 @@ def _on_request_finished( if response: response._finished_future.set_result(True) - def _on_console_message(self, event: Dict) -> None: + def _on_console_message(self, event: "channels.BrowserContextConsoleEvent") -> None: message = ConsoleMessage(event, self._loop, self._dispatcher_fiber) worker = message.worker if worker: @@ -679,7 +687,9 @@ def _on_dialog(self, dialog: Dialog) -> None: # a) removing "dialog" listener subscription (client->server) # b) actual "dialog" event (server->client) if dialog.type == "beforeunload": - create_task_and_ignore_exception(self._loop, dialog.accept()) + create_task_and_ignore_exception( + self._loop, dialog.accept(prompt_text=None) + ) else: create_task_and_ignore_exception(self._loop, dialog.dismiss()) @@ -719,14 +729,13 @@ def service_workers(self) -> List[Worker]: async def new_cdp_session(self, page: Union[Page, Frame]) -> CDPSession: page = to_impl(page) - params = {} if isinstance(page, Page): - params["page"] = page._channel + result = await self._channel.newCDPSession(page=page._channel, frame=None) elif isinstance(page, Frame): - params["frame"] = page._channel + result = await self._channel.newCDPSession(page=None, frame=page._channel) else: raise Error("page: expected Page or Frame") - return from_channel(await self._channel.send("newCDPSession", None, params)) + return from_channel(result["session"]) @property def tracing(self) -> Tracing: @@ -747,3 +756,35 @@ def clock(self) -> Clock: @property def credentials(self) -> Credentials: return self._credentials + + +def to_record_video_params( + record_video_dir: Optional[Union[str, Path]], + record_video_size: Optional[RecordVideoSize], + record_video_fps: Optional[int], +) -> "Optional[channels.BrowserNewContextParamsRecordVideo]": + if record_video_dir is None: + return None + return { + "dir": str(Path(record_video_dir).absolute()), + "size": record_video_size, + "fps": record_video_fps, + } + + +def to_accept_downloads_params( + accept_downloads: Optional[bool], +) -> Optional[Literal["accept", "deny"]]: + if accept_downloads is None: + return None + return "accept" if accept_downloads else "deny" + + +async def to_storage_state_params( + storage_state: Optional[Union[StorageState, str, Path]], +) -> "Optional[channels.BrowserNewContextParamsStorageState]": + if storage_state is None: + return None + if isinstance(storage_state, dict): + return cast("channels.BrowserNewContextParamsStorageState", storage_state) + return json.loads((await async_readfile(storage_state)).decode()) diff --git a/playwright/_impl/_browser_type.py b/playwright/_impl/_browser_type.py index 80ba45a28..238fab608 100644 --- a/playwright/_impl/_browser_type.py +++ b/playwright/_impl/_browser_type.py @@ -13,7 +13,6 @@ # limitations under the License. import asyncio -import json import pathlib from pathlib import Path from typing import TYPE_CHECKING, Dict, List, Optional, Pattern, Sequence, Union, cast @@ -26,7 +25,11 @@ ViewportSize, ) from playwright._impl._browser import Browser -from playwright._impl._browser_context import BrowserContext +from playwright._impl._browser_context import ( + BrowserContext, + to_accept_downloads_params, + to_record_video_params, +) from playwright._impl._connection import ChannelOwner, Connection, from_channel from playwright._impl._errors import Error from playwright._impl._headers import serialize_headers @@ -40,8 +43,6 @@ HarMode, ReducedMotion, ServiceWorkersPolicy, - async_readfile, - locals_to_params, ) from playwright._impl._json_pipe import JsonPipeTransport from playwright._impl._network import ( @@ -51,11 +52,15 @@ from playwright._impl._timeout_settings import TimeoutSettings from playwright._impl._waiter import throw_on_timeout -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._playwright import Playwright + from playwright._impl._protocol_channels import BrowserTypeChannel class BrowserType(ChannelOwner): + _channel: "BrowserTypeChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -75,142 +80,225 @@ def executable_path(self) -> str: async def launch( self, - executablePath: Union[str, Path] = None, - channel: str = None, - args: Sequence[str] = None, - ignoreDefaultArgs: Union[bool, Sequence[str]] = None, - handleSIGINT: bool = None, - handleSIGTERM: bool = None, - handleSIGHUP: bool = None, - timeout: float = None, - env: Env = None, - headless: bool = None, - proxy: ProxySettings = None, - downloadsPath: Union[str, Path] = None, - slowMo: float = None, - tracesDir: Union[pathlib.Path, str] = None, - artifactsDir: Union[pathlib.Path, str] = None, - chromiumSandbox: bool = None, - firefoxUserPrefs: Dict[str, Union[str, float, bool]] = None, + executable_path: Optional[Union[str, Path]], + channel: Optional[str], + args: Optional[Sequence[str]], + ignore_default_args: Optional[Union[bool, Sequence[str]]], + handle_sigint: Optional[bool], + handle_sigterm: Optional[bool], + handle_sighup: Optional[bool], + timeout: Optional[float], + env: Optional[Env], + headless: Optional[bool], + proxy: Optional[ProxySettings], + downloads_path: Optional[Union[str, Path]], + slow_mo: Optional[float], + traces_dir: Optional[Union[pathlib.Path, str]], + artifacts_dir: Optional[Union[pathlib.Path, str]], + chromium_sandbox: Optional[bool], + firefox_user_prefs: Optional[Dict[str, Union[str, float, bool]]], ) -> Browser: - params = locals_to_params(locals()) - normalize_launch_params(params) - browser = cast( - Browser, - from_channel( - await self._channel.send( - "launch", TimeoutSettings.launch_timeout, params - ) + result = await self._channel.launch( + channel=channel, + executablePath=to_path_param(executable_path), + args=args, + ignoreAllDefaultArgs=True if ignore_default_args is True else None, + ignoreDefaultArgs=( + None if isinstance(ignore_default_args, bool) else ignore_default_args ), + handleSIGINT=handle_sigint, + handleSIGTERM=handle_sigterm, + handleSIGHUP=handle_sighup, + env=to_env_params(env), + headless=headless, + proxy=cast("Optional[channels.BrowserTypeLaunchParamsProxy]", proxy), + downloadsPath=to_path_param(downloads_path), + tracesDir=to_path_param(traces_dir), + artifactsDir=to_path_param(artifacts_dir), + chromiumSandbox=chromium_sandbox, + firefoxUserPrefs=firefox_user_prefs, + cdpPort=None, + slowMo=slow_mo, + timeout=TimeoutSettings.launch_timeout(timeout), ) + browser = cast(Browser, from_channel(result["browser"])) browser._connect_to_browser_type( - self, str(tracesDir) if tracesDir is not None else None + self, str(traces_dir) if traces_dir is not None else None ) return browser async def launch_persistent_context( self, - userDataDir: Union[str, Path], - channel: str = None, - executablePath: Union[str, Path] = None, - args: Sequence[str] = None, - ignoreDefaultArgs: Union[bool, Sequence[str]] = None, - handleSIGINT: bool = None, - handleSIGTERM: bool = None, - handleSIGHUP: bool = None, - timeout: float = None, - env: Env = None, - headless: bool = None, - proxy: ProxySettings = None, - downloadsPath: Union[str, Path] = None, - slowMo: float = None, - viewport: ViewportSize = None, - screen: ViewportSize = None, - noViewport: bool = None, - ignoreHTTPSErrors: bool = None, - javaScriptEnabled: bool = None, - bypassCSP: bool = None, - userAgent: str = None, - locale: str = None, - timezoneId: str = None, - geolocation: Geolocation = None, - permissions: Sequence[str] = None, - extraHTTPHeaders: Dict[str, str] = None, - offline: bool = None, - httpCredentials: Union[HttpCredentials, Sequence[HttpCredentials]] = None, - deviceScaleFactor: float = None, - isMobile: bool = None, - hasTouch: bool = None, - colorScheme: ColorScheme = None, - reducedMotion: ReducedMotion = None, - forcedColors: ForcedColors = None, - contrast: Contrast = None, - acceptDownloads: bool = None, - tracesDir: Union[pathlib.Path, str] = None, - artifactsDir: Union[pathlib.Path, str] = None, - chromiumSandbox: bool = None, - firefoxUserPrefs: Dict[str, Union[str, float, bool]] = None, - recordHarPath: Union[Path, str] = None, - recordHarOmitContent: bool = None, - recordVideoDir: Union[Path, str] = None, - recordVideoSize: ViewportSize = None, - baseURL: str = None, - strictSelectors: bool = None, - serviceWorkers: ServiceWorkersPolicy = None, - recordHarUrlFilter: Union[Pattern[str], str] = None, - recordHarMode: HarMode = None, - recordHarContent: HarContentPolicy = None, - clientCertificates: List[ClientCertificate] = None, - recordVideoFps: int = None, + user_data_dir: Union[str, Path], + channel: Optional[str], + executable_path: Optional[Union[str, Path]], + args: Optional[Sequence[str]], + ignore_default_args: Optional[Union[bool, Sequence[str]]], + handle_sigint: Optional[bool], + handle_sigterm: Optional[bool], + handle_sighup: Optional[bool], + timeout: Optional[float], + env: Optional[Env], + headless: Optional[bool], + proxy: Optional[ProxySettings], + downloads_path: Optional[Union[str, Path]], + slow_mo: Optional[float], + viewport: Optional[ViewportSize], + screen: Optional[ViewportSize], + no_viewport: Optional[bool], + ignore_https_errors: Optional[bool], + java_script_enabled: Optional[bool], + bypass_csp: Optional[bool], + user_agent: Optional[str], + locale: Optional[str], + timezone_id: Optional[str], + geolocation: Optional[Geolocation], + permissions: Optional[Sequence[str]], + extra_http_headers: Optional[Dict[str, str]], + offline: Optional[bool], + http_credentials: Optional[Union[HttpCredentials, Sequence[HttpCredentials]]], + device_scale_factor: Optional[float], + is_mobile: Optional[bool], + has_touch: Optional[bool], + color_scheme: Optional[ColorScheme], + reduced_motion: Optional[ReducedMotion], + forced_colors: Optional[ForcedColors], + contrast: Optional[Contrast], + accept_downloads: Optional[bool], + traces_dir: Optional[Union[pathlib.Path, str]], + artifacts_dir: Optional[Union[pathlib.Path, str]], + chromium_sandbox: Optional[bool], + firefox_user_prefs: Optional[Dict[str, Union[str, float, bool]]], + record_har_path: Optional[Union[Path, str]], + record_har_omit_content: Optional[bool], + record_video_dir: Optional[Union[Path, str]], + record_video_size: Optional[ViewportSize], + base_url: Optional[str], + strict_selectors: Optional[bool], + service_workers: Optional[ServiceWorkersPolicy], + record_har_url_filter: Optional[Union[Pattern[str], str]], + record_har_mode: Optional[HarMode], + record_har_content: Optional[HarContentPolicy], + client_certificates: Optional[List[ClientCertificate]], + record_video_fps: Optional[int], ) -> BrowserContext: - userDataDir = self._user_data_dir(userDataDir) - params = locals_to_params(locals()) - await self._prepare_browser_context_params(params) - normalize_launch_params(params) - result = await self._channel.send_return_as_dict( - "launchPersistentContext", TimeoutSettings.launch_timeout, params - ) - browser = cast( - Browser, - from_channel(result["browser"]), + selectors = self._playwright.selectors + result = await self._channel.launchPersistentContext( + channel=channel, + executablePath=to_path_param(executable_path), + args=args, + ignoreAllDefaultArgs=True if ignore_default_args is True else None, + ignoreDefaultArgs=( + None if isinstance(ignore_default_args, bool) else ignore_default_args + ), + handleSIGINT=handle_sigint, + handleSIGTERM=handle_sigterm, + handleSIGHUP=handle_sighup, + env=to_env_params(env), + headless=headless, + proxy=cast( + "Optional[channels.BrowserTypeLaunchPersistentContextParamsProxy]", + proxy, + ), + downloadsPath=to_path_param(downloads_path), + tracesDir=to_path_param(traces_dir), + artifactsDir=to_path_param(artifacts_dir), + chromiumSandbox=chromium_sandbox, + firefoxUserPrefs=firefox_user_prefs, + cdpPort=None, + noDefaultViewport=True if no_viewport else None, + viewport=viewport, + screen=screen, + ignoreHTTPSErrors=ignore_https_errors, + clientCertificates=cast( + "Optional[List[channels.BrowserTypeLaunchPersistentContextParamsClientCertificates]]", + await to_client_certificates_protocol(client_certificates), + ), + javaScriptEnabled=java_script_enabled, + bypassCSP=bypass_csp, + userAgent=user_agent, + locale=locale, + timezoneId=timezone_id, + geolocation=cast( + "Optional[channels.BrowserTypeLaunchPersistentContextParamsGeolocation]", + geolocation, + ), + permissions=permissions, + extraHTTPHeaders=( + serialize_headers(extra_http_headers) + if extra_http_headers is not None + else None + ), + offline=offline, + httpCredentials=cast( + "Optional[List[channels.HttpCredentials]]", + to_http_credentials_protocol(http_credentials), + ), + deviceScaleFactor=device_scale_factor, + isMobile=is_mobile, + hasTouch=has_touch, + colorScheme="no-override" if color_scheme == "null" else color_scheme, + reducedMotion=( + "no-override" if reduced_motion == "null" else reduced_motion + ), + forcedColors="no-override" if forced_colors == "null" else forced_colors, + acceptDownloads=to_accept_downloads_params(accept_downloads), + contrast="no-override" if contrast == "null" else contrast, + baseURL=base_url, + recordVideo=to_record_video_params( + record_video_dir, record_video_size, record_video_fps + ), + strictSelectors=strict_selectors, + serviceWorkers=service_workers, + selectorEngines=selectors._selector_engines, + testIdAttributeName=selectors._test_id_attribute_name, + userDataDir=self._user_data_dir(user_data_dir), + slowMo=slow_mo, + timeout=TimeoutSettings.launch_timeout(timeout), ) + browser = cast(Browser, from_channel(result["browser"])) browser._connect_to_browser_type( - self, str(tracesDir) if tracesDir is not None else None + self, str(traces_dir) if traces_dir is not None else None ) context = cast(BrowserContext, from_channel(result["context"])) await context._initialize_har_from_options( - record_har_content=recordHarContent, - record_har_mode=recordHarMode, - record_har_omit_content=recordHarOmitContent, - record_har_path=recordHarPath, - record_har_url_filter=recordHarUrlFilter, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, ) return context - def _user_data_dir(self, userDataDir: Optional[Union[str, Path]]) -> str: - if not userDataDir: + def _user_data_dir(self, user_data_dir: Optional[Union[str, Path]]) -> str: + if not user_data_dir: return "" - if not Path(userDataDir).is_absolute(): - return str(Path(userDataDir).resolve()) - return str(Path(userDataDir)) + if not Path(user_data_dir).is_absolute(): + return str(Path(user_data_dir).resolve()) + return str(Path(user_data_dir)) async def connect_over_cdp( self, - endpointURL: str, - timeout: float = None, - slowMo: float = None, - headers: Dict[str, str] = None, - isLocal: bool = None, - noDefaults: bool = None, - artifactsDir: Union[str, Path] = None, + endpoint_url: str, + timeout: Optional[float], + slow_mo: Optional[float], + headers: Optional[Dict[str, str]], + is_local: Optional[bool], + no_defaults: Optional[bool], + artifacts_dir: Optional[Union[str, Path]], ) -> Browser: - params = locals_to_params(locals()) - if params.get("headers"): - params["headers"] = serialize_headers(params["headers"]) - response = await self._channel.send_return_as_dict( - "connectOverCDP", TimeoutSettings.launch_timeout, params + result = await self._channel.connectOverCDP( + endpointURL=endpoint_url, + headers=serialize_headers(headers) if headers is not None else None, + slowMo=slow_mo, + isLocal=is_local, + noDefaults=no_defaults, + isWebView=None, + artifactsDir=str(artifacts_dir) if artifacts_dir is not None else None, + transport=None, + timeout=TimeoutSettings.launch_timeout(timeout), ) - browser = cast(Browser, from_channel(response["browser"])) + browser = cast(Browser, from_channel(result["browser"])) browser._connect_to_browser_type(self, None) return browser @@ -218,32 +306,27 @@ async def connect_over_cdp( async def connect( self, endpoint: str, - timeout: float = None, - slowMo: float = None, - headers: Dict[str, str] = None, - exposeNetwork: str = None, + timeout: Optional[float], + slow_mo: Optional[float], + headers: Optional[Dict[str, str]], + expose_network: Optional[str], ) -> Browser: - if slowMo is None: - slowMo = 0 + if slow_mo is None: + slow_mo = 0 if timeout is None: timeout = 0 headers = {**(headers if headers else {}), "x-playwright-browser": self.name} local_utils = self._connection.local_utils - pipe_channel = ( - await local_utils._channel.send_return_as_dict( - "connect", - lambda t: t or 0, - { - "endpoint": endpoint, - "headers": headers, - "slowMo": slowMo, - "timeout": timeout, - "exposeNetwork": exposeNetwork, - }, - ) - )["pipe"] - transport = JsonPipeTransport(self._connection._loop, pipe_channel) + result = await local_utils._channel.connect( + endpoint=endpoint, + headers=headers, + exposeNetwork=expose_network, + slowMo=slow_mo, + socksProxyRedirectPortForTest=None, + timeout=timeout, + ) + transport = JsonPipeTransport(self._connection._loop, result["pipe"]) connection = Connection( self._connection._dispatcher_fiber, @@ -301,80 +384,12 @@ def handle_transport_close(reason: Optional[str]) -> None: return browser - async def _prepare_browser_context_params(self, params: Dict) -> None: - if params.get("noViewport"): - del params["noViewport"] - params["noDefaultViewport"] = True - if "defaultBrowserType" in params: - del params["defaultBrowserType"] - if "extraHTTPHeaders" in params: - params["extraHTTPHeaders"] = serialize_headers(params["extraHTTPHeaders"]) - if "recordVideoDir" in params: - params["recordVideo"] = {"dir": Path(params["recordVideoDir"]).absolute()} - if "recordVideoSize" in params: - params["recordVideo"]["size"] = params["recordVideoSize"] - del params["recordVideoSize"] - if "recordVideoFps" in params: - params["recordVideo"]["fps"] = params["recordVideoFps"] - del params["recordVideoDir"] - params.pop("recordVideoFps", None) - if "storageState" in params: - storageState = params["storageState"] - if not isinstance(storageState, dict): - params["storageState"] = json.loads( - (await async_readfile(storageState)).decode() - ) - if params.get("colorScheme", None) == "null": - params["colorScheme"] = "no-override" - if params.get("reducedMotion", None) == "null": - params["reducedMotion"] = "no-override" - if params.get("forcedColors", None) == "null": - params["forcedColors"] = "no-override" - if params.get("contrast", None) == "null": - params["contrast"] = "no-override" - if "acceptDownloads" in params: - params["acceptDownloads"] = ( - "accept" if params["acceptDownloads"] else "deny" - ) - - if "clientCertificates" in params: - params["clientCertificates"] = await to_client_certificates_protocol( - params["clientCertificates"] - ) - if "httpCredentials" in params: - params["httpCredentials"] = to_http_credentials_protocol( - params["httpCredentials"] - ) - params["selectorEngines"] = self._playwright.selectors._selector_engines - params["testIdAttributeName"] = ( - self._playwright.selectors._test_id_attribute_name - ) - # Remove HAR options - params.pop("recordHarPath", None) - params.pop("recordHarOmitContent", None) - params.pop("recordHarUrlFilter", None) - params.pop("recordHarMode", None) - params.pop("recordHarContent", None) +def to_env_params(env: Optional[Env]) -> Optional[List["channels.NameValue"]]: + if env is None: + return None + return [{"name": name, "value": str(value)} for name, value in env.items()] -def normalize_launch_params(params: Dict) -> None: - if "env" in params: - params["env"] = [ - {"name": name, "value": str(value)} - for [name, value] in params["env"].items() - ] - if "ignoreDefaultArgs" in params: - if params["ignoreDefaultArgs"] is True: - params["ignoreAllDefaultArgs"] = True - del params["ignoreDefaultArgs"] - elif params["ignoreDefaultArgs"] is False: - del params["ignoreDefaultArgs"] - if "executablePath" in params: - params["executablePath"] = str(Path(params["executablePath"])) - if "downloadsPath" in params: - params["downloadsPath"] = str(Path(params["downloadsPath"])) - if "tracesDir" in params: - params["tracesDir"] = str(Path(params["tracesDir"])) - if "artifactsDir" in params: - params["artifactsDir"] = str(Path(params["artifactsDir"])) +def to_path_param(path: Optional[Union[str, Path]]) -> Optional[str]: + return str(Path(path)) if path is not None else None diff --git a/playwright/_impl/_cdp_session.py b/playwright/_impl/_cdp_session.py index 07d291ae2..1fdf77d9d 100644 --- a/playwright/_impl/_cdp_session.py +++ b/playwright/_impl/_cdp_session.py @@ -13,13 +13,18 @@ # limitations under the License. from types import SimpleNamespace -from typing import Any, Dict +from typing import TYPE_CHECKING, Dict, Optional from playwright._impl._connection import ChannelOwner -from playwright._impl._helper import locals_to_params + +if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels + from playwright._impl._protocol_channels import CDPSessionChannel class CDPSession(ChannelOwner): + _channel: "CDPSessionChannel" + Events = SimpleNamespace( Event="event", Close="close", @@ -29,18 +34,15 @@ def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: super().__init__(parent, type, guid, initializer) - self._channel.on("event", lambda params: self._on_event(params)) - self._channel.on("close", lambda _: self.emit(CDPSession.Events.Close, self)) + self._channel.onEvent(self._on_event) + self._channel.onClose(lambda: self.emit(CDPSession.Events.Close, self)) - def _on_event(self, params: Any) -> None: - self.emit(params["method"], params.get("params")) - self.emit(CDPSession.Events.Event, params) + def _on_event(self, event: "channels.CDPSessionEventEvent") -> None: + self.emit(event["method"], event["params"]) + self.emit(CDPSession.Events.Event, event) - async def send(self, method: str, params: Dict = None) -> Dict: - return await self._channel.send("send", None, locals_to_params(locals())) + async def send(self, method: str, params: Optional[Dict]) -> Dict: + return (await self._channel.send(method=method, params=params))["result"] async def detach(self) -> None: - await self._channel.send( - "detach", - None, - ) + await self._channel.detach() diff --git a/playwright/_impl/_clock.py b/playwright/_impl/_clock.py index f6eb7c42d..cbdfd51b7 100644 --- a/playwright/_impl/_clock.py +++ b/playwright/_impl/_clock.py @@ -13,9 +13,9 @@ # limitations under the License. import datetime -from typing import TYPE_CHECKING, Dict, Union +from typing import TYPE_CHECKING, Optional, Tuple, Union -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from playwright._impl._browser_context import BrowserContext @@ -25,80 +25,80 @@ def __init__(self, browser_context: "BrowserContext") -> None: self._loop = browser_context._loop self._dispatcher_fiber = browser_context._dispatcher_fiber - async def install(self, time: Union[float, str, datetime.datetime] = None) -> None: - await self._browser_context._channel.send( - "clockInstall", - None, - parse_time(time) if time is not None else {}, + async def install( + self, time: Optional[Union[float, str, datetime.datetime]] + ) -> None: + time_number, time_string = ( + parse_time(time) if time is not None else (None, None) + ) + await self._browser_context._channel.clockInstall( + timeNumber=time_number, timeString=time_string ) async def fast_forward( self, ticks: Union[int, str], ) -> None: - await self._browser_context._channel.send( - "clockFastForward", - None, - parse_ticks(ticks), + ticks_number, ticks_string = parse_ticks(ticks) + await self._browser_context._channel.clockFastForward( + ticksNumber=ticks_number, ticksString=ticks_string ) async def pause_at( self, time: Union[float, str, datetime.datetime], ) -> None: - await self._browser_context._channel.send( - "clockPauseAt", - None, - parse_time(time), + time_number, time_string = parse_time(time) + await self._browser_context._channel.clockPauseAt( + timeNumber=time_number, timeString=time_string ) async def resume( self, ) -> None: - await self._browser_context._channel.send("clockResume", None) + await self._browser_context._channel.clockResume() async def run_for( self, ticks: Union[int, str], ) -> None: - await self._browser_context._channel.send( - "clockRunFor", - None, - parse_ticks(ticks), + ticks_number, ticks_string = parse_ticks(ticks) + await self._browser_context._channel.clockRunFor( + ticksNumber=ticks_number, ticksString=ticks_string ) async def set_fixed_time( self, time: Union[float, str, datetime.datetime], ) -> None: - await self._browser_context._channel.send( - "clockSetFixedTime", - None, - parse_time(time), + time_number, time_string = parse_time(time) + await self._browser_context._channel.clockSetFixedTime( + timeNumber=time_number, timeString=time_string ) async def set_system_time( self, time: Union[float, str, datetime.datetime], ) -> None: - await self._browser_context._channel.send( - "clockSetSystemTime", - None, - parse_time(time), + time_number, time_string = parse_time(time) + await self._browser_context._channel.clockSetSystemTime( + timeNumber=time_number, timeString=time_string ) def parse_time( time: Union[float, str, datetime.datetime], -) -> Dict[str, Union[int, str]]: +) -> Tuple[Optional[int], Optional[str]]: + """Returns (timeNumber, timeString).""" if isinstance(time, (float, int)): - return {"timeNumber": int(time * 1_000)} + return int(time * 1_000), None if isinstance(time, str): - return {"timeString": time} - return {"timeNumber": int(time.timestamp() * 1_000)} + return None, time + return int(time.timestamp() * 1_000), None -def parse_ticks(ticks: Union[int, str]) -> Dict[str, Union[int, str]]: +def parse_ticks(ticks: Union[int, str]) -> Tuple[Optional[int], Optional[str]]: + """Returns (ticksNumber, ticksString).""" if isinstance(ticks, int): - return {"ticksNumber": ticks} - return {"ticksString": ticks} + return ticks, None + return None, ticks diff --git a/playwright/_impl/_connection.py b/playwright/_impl/_connection.py index c0b01b2cc..325276959 100644 --- a/playwright/_impl/_connection.py +++ b/playwright/_impl/_connection.py @@ -49,7 +49,6 @@ from playwright._impl._greenlets import EventGreenlet from playwright._impl._helper import ( ParsedMessagePayload, - create_task_and_ignore_exception, ) from playwright._impl._protocol_check import check_message, is_enabled from playwright._impl._transport import Transport @@ -57,96 +56,76 @@ if TYPE_CHECKING: from playwright._impl._local_utils import LocalUtils from playwright._impl._playwright import Playwright + from playwright._impl._protocol_channels import RootChannel -TimeoutCalculator = Optional[Callable[[Optional[float]], float]] _PLAYWRIGHT_MODULE_PATH = str(Path(playwright.__file__).parents[0]) -class Channel(AsyncIOEventEmitter): +class Channel: + """The base of the generated channels in _protocol_channels.py. + + Port of the channel proxy in packages/playwright-core/src/client/channelOwner.ts. + """ + def __init__(self, connection: "Connection", object: "ChannelOwner") -> None: - super().__init__() self._connection = connection self._guid = object._guid self._object = object - self.on("error", lambda exc: self._connection._on_event_listener_error(exc)) - - async def send( - self, - method: str, - timeout_calculator: TimeoutCalculator, - params: Dict = None, - is_internal: bool = False, - title: str = None, - ) -> Any: - return await self._connection.wrap_api_call( - lambda: self._inner_send(method, timeout_calculator, params, False), - is_internal, - title, + self._emitter = AsyncIOEventEmitter() + self._emitter.on( + "error", lambda exc: self._connection._on_event_listener_error(exc) ) + self._event_listeners: Dict[Tuple[str, Any], Callable[[Any], Any]] = {} + + async def _call( + self, method: str, params: Mapping[str, Any], timeout: float + ) -> Dict[str, Any]: + """Sends a command and returns its result, {} when it has none.""" + result = await self._connection.wrap_api_call( + lambda: self._inner_send(method, _filter_none(params), timeout) + ) + return result or {} - def send_may_fail( - self, - method: str, - timeout_calculator: TimeoutCalculator, - params: Dict = None, - is_internal: bool = False, - title: str = None, + def _add_event_listener( + self, event: str, handler: Any, listener: Callable[[Any], Any] ) -> None: - create_task_and_ignore_exception( - self._connection._loop, - self.send(method, timeout_calculator, params, is_internal, title), - ) + self._event_listeners[(event, handler)] = listener + self._emitter.on(event, listener) - async def send_return_as_dict( - self, - method: str, - timeout_calculator: TimeoutCalculator, - params: Dict = None, - is_internal: bool = False, - title: str = None, - ) -> Any: - return await self._connection.wrap_api_call( - lambda: self._inner_send(method, timeout_calculator, params, True), - is_internal, - title, - ) + def _remove_event_listener(self, event: str, handler: Any) -> None: + listener = self._event_listeners.pop((event, handler), None) + if listener: + self._emitter.remove_listener(event, listener) def send_no_reply( self, method: str, - timeout_calculator: TimeoutCalculator, - params: Dict = None, + params: Mapping[str, Any] = None, is_internal: bool = False, title: str = None, ) -> None: - # No reply messages are used to e.g. __waitInfo__(after). - augmented_params, timeout = _augment_params(params, timeout_calculator) + """Sends a message without waiting for the reply. + + For messages that must go out synchronously, such as __waitInfo__ and + updateSubscription. + """ self._connection.wrap_api_call_sync( lambda: self._connection._send_message_to_server( - self._object, - method, - augmented_params, - timeout, - True, + self._object, method, _filter_none(params or {}), 0, True ), is_internal, title, ) async def _inner_send( - self, - method: str, - timeout_calculator: TimeoutCalculator, - params: Optional[Dict], - return_as_dict: bool, - ) -> Any: + self, method: str, params: Dict[str, Any], timeout: float + ) -> Optional[Dict[str, Any]]: if self._connection._error: error = self._connection._error self._connection._error = None raise error - augmented_params, timeout = _augment_params(params, timeout_calculator) callback = self._connection._send_message_to_server( - self._object, method, augmented_params, timeout + self._object, method, params, timeout ) try: done, _ = await asyncio.wait( @@ -166,18 +145,10 @@ async def _inner_send( if not callback.future.done(): callback.future.cancel() result = next(iter(done)).result() - # Protocol now has named return values, assume result is one level deeper unless - # there is explicit ambiguity. if not result: return None assert isinstance(result, dict) - if return_as_dict: - return result - if len(result) == 0: - return None - assert len(result) == 1 - key = next(iter(result)) - return result[key] + return result class ChannelOwner(AsyncIOEventEmitter): @@ -200,7 +171,12 @@ def __init__( parent if isinstance(parent, ChannelOwner) else None ) self._objects: Dict[str, "ChannelOwner"] = {} - self._channel: Channel = Channel(self._connection, self) + # Imported here: the generated channels import this module. + from playwright._impl._protocol_channels import CHANNEL_CLASSES + + self._channel: Channel = CHANNEL_CLASSES.get(type, Channel)( + self._connection, self + ) self._initializer = initializer self._was_collected = False @@ -236,7 +212,6 @@ def _update_subscription(self, event: str, enabled: bool) -> None: self._connection.wrap_api_call_sync( lambda: self._channel.send_no_reply( "updateSubscription", - None, {"event": protocol_event, "enabled": enabled}, ), True, @@ -282,19 +257,14 @@ def cb(task: asyncio.Task) -> None: class RootChannelOwner(ChannelOwner): + _channel: "RootChannel" + def __init__(self, connection: "Connection") -> None: super().__init__(connection, "Root", "", {}) async def initialize(self) -> "Playwright": - return from_channel( - await self._channel.send( - "initialize", - None, - { - "sdkLanguage": "python", - }, - ) - ) + result = await self._channel.initialize(sdkLanguage="python") + return from_channel(result["playwright"]) class Connection(EventEmitter): @@ -510,7 +480,7 @@ def dispatch(self, msg: ParsedMessagePayload) -> None: error = msg.get("error") if error and not msg.get("result"): parsed_error = parse_error( - error["error"], format_call_log(msg.get("log")) # type: ignore + error["error"], format_call_log(msg.get("log")) ) parsed_error._log = msg.get("log") parsed_error._details = self._replace_guids_with_channels( @@ -554,7 +524,7 @@ def dispatch(self, msg: ParsedMessagePayload) -> None: should_replace_guids_with_channels = "jsonPipe@" not in guid try: if self._is_sync: - for listener in object._channel.listeners(method): + for listener in object._channel._emitter.listeners(method): # Event handlers like route/locatorHandlerTriggered require us to perform async work. # In order to report their potential errors to the user, we need to catch it and store it in the connection def _done_callback(future: asyncio.Future) -> None: @@ -577,11 +547,11 @@ def _listener_with_error_handler_attached(params: Any) -> None: g.switch(params) else: if should_replace_guids_with_channels: - object._channel.emit( + object._channel._emitter.emit( method, self._replace_guids_with_channels(params) ) else: - object._channel.emit(method, params) + object._channel._emitter.emit(method, params) except BaseException as exc: self._on_event_listener_error(exc) @@ -752,19 +722,6 @@ def _attach_api_call_information( return stack_trace_information -def _augment_params( - params: Optional[Dict], - timeout_calculator: Optional[Callable[[Optional[float]], float]], -) -> Tuple[Dict, float]: - if params is None: - params = {} - timeout_param = params.pop("timeout", None) - timeout: float = 0 - if timeout_calculator: - timeout = timeout_calculator(timeout_param) - return _filter_none(params), timeout - - def _filter_none(d: Mapping) -> Dict: result = {} for k, v in d.items(): diff --git a/playwright/_impl/_console_message.py b/playwright/_impl/_console_message.py index 66879194e..a60ef1493 100644 --- a/playwright/_impl/_console_message.py +++ b/playwright/_impl/_console_message.py @@ -13,26 +13,53 @@ # limitations under the License. from asyncio import AbstractEventLoop -from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union +from typing import TYPE_CHECKING, Any, List, Literal, Optional, cast from playwright._impl._api_structures import SourceLocation from playwright._impl._connection import from_channel, from_nullable_channel from playwright._impl._js_handle import JSHandle if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._page import Page from playwright._impl._worker import Worker +ConsoleMessageType = Literal[ + "assert", + "clear", + "count", + "debug", + "dir", + "dirxml", + "endGroup", + "error", + "info", + "log", + "profile", + "profileEnd", + "startGroup", + "startGroupCollapsed", + "table", + "time", + "timeEnd", + "trace", + "warning", +] + + class ConsoleMessage: def __init__( - self, event: Dict, loop: AbstractEventLoop, dispatcher_fiber: Any + self, + event: "channels.BrowserContextConsoleEvent", + loop: AbstractEventLoop, + dispatcher_fiber: Any, ) -> None: self._event = event self._loop = loop self._dispatcher_fiber = dispatcher_fiber - self._page: Optional["Page"] = from_nullable_channel(event.get("page")) - self._worker: Optional["Worker"] = from_nullable_channel(event.get("worker")) + self._page: Optional["Page"] = from_nullable_channel(event["page"]) + self._worker: Optional["Worker"] = from_nullable_channel(event["worker"]) def __repr__(self) -> str: return f"" @@ -41,28 +68,8 @@ def __str__(self) -> str: return self.text @property - def type(self) -> Union[ - Literal["assert"], - Literal["clear"], - Literal["count"], - Literal["debug"], - Literal["dir"], - Literal["dirxml"], - Literal["endGroup"], - Literal["error"], - Literal["info"], - Literal["log"], - Literal["profile"], - Literal["profileEnd"], - Literal["startGroup"], - Literal["startGroupCollapsed"], - Literal["table"], - Literal["time"], - Literal["timeEnd"], - Literal["trace"], - Literal["warning"], - ]: - return self._event["type"] + def type(self) -> ConsoleMessageType: + return cast(ConsoleMessageType, self._event["type"]) @property def text(self) -> str: diff --git a/playwright/_impl/_credentials.py b/playwright/_impl/_credentials.py index ad0245291..17337a8a1 100644 --- a/playwright/_impl/_credentials.py +++ b/playwright/_impl/_credentials.py @@ -12,12 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING, List, Optional from playwright._impl._api_structures import VirtualCredential -from playwright._impl._helper import locals_to_params -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from playwright._impl._browser_context import BrowserContext @@ -28,31 +27,34 @@ def __init__(self, browser_context: "BrowserContext") -> None: self._dispatcher_fiber = browser_context._dispatcher_fiber async def install(self) -> None: - await self._browser_context._channel.send("credentialsInstall", None) + await self._browser_context._channel.credentialsInstall() async def create( self, - rpId: str, - id: str = None, - userHandle: str = None, - privateKey: str = None, - publicKey: str = None, - signCount: int = None, + rp_id: str, + id: Optional[str], + user_handle: Optional[str], + private_key: Optional[str], + public_key: Optional[str], + sign_count: Optional[int], ) -> VirtualCredential: - result = await self._browser_context._channel.send_return_as_dict( - "credentialsCreate", None, locals_to_params(locals()) + result = await self._browser_context._channel.credentialsCreate( + rpId=rp_id, + id=id, + userHandle=user_handle, + privateKey=private_key, + publicKey=public_key, + signCount=sign_count, ) - return (result or {})["credential"] + return result["credential"] async def delete(self, id: str) -> None: - await self._browser_context._channel.send("credentialsDelete", None, {"id": id}) + await self._browser_context._channel.credentialsDelete(id=id) async def get( self, - rpId: str = None, - id: str = None, + rp_id: Optional[str], + id: Optional[str], ) -> List[VirtualCredential]: - result = await self._browser_context._channel.send_return_as_dict( - "credentialsGet", None, locals_to_params(locals()) - ) - return (result or {}).get("credentials", []) + result = await self._browser_context._channel.credentialsGet(rpId=rp_id, id=id) + return result["credentials"] diff --git a/playwright/_impl/_debugger.py b/playwright/_impl/_debugger.py index 36e4bd989..b8e30c064 100644 --- a/playwright/_impl/_debugger.py +++ b/playwright/_impl/_debugger.py @@ -13,13 +13,19 @@ # limitations under the License. from types import SimpleNamespace -from typing import Any, Dict, Optional +from typing import TYPE_CHECKING, Dict, Optional, cast from playwright._impl._api_structures import DebuggerLocation, DebuggerPausedDetails from playwright._impl._connection import ChannelOwner +if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels + from playwright._impl._protocol_channels import DebuggerChannel + class Debugger(ChannelOwner): + _channel: "DebuggerChannel" + Events = SimpleNamespace( PausedStateChanged="pausedstatechanged", ) @@ -29,25 +35,33 @@ def __init__( ) -> None: super().__init__(parent, type, guid, initializer) self._paused_details: Optional[DebuggerPausedDetails] = None - self._channel.on( - "pausedStateChanged", lambda params: self._on_paused_state_changed(params) - ) + self._channel.onPausedStateChanged(self._on_paused_state_changed) - def _on_paused_state_changed(self, params: Dict[str, Any]) -> None: - self._paused_details = params.get("pausedDetails") + def _on_paused_state_changed( + self, event: "channels.DebuggerPausedStateChangedEvent" + ) -> None: + self._paused_details = cast( + Optional[DebuggerPausedDetails], event["pausedDetails"] + ) self.emit(Debugger.Events.PausedStateChanged) async def request_pause(self) -> None: - await self._channel.send("requestPause", None) + await self._channel.requestPause() async def resume(self) -> None: - await self._channel.send("resume", None) + await self._channel.resume() async def next(self) -> None: - await self._channel.send("next", None) + await self._channel.next() async def run_to(self, location: DebuggerLocation) -> None: - await self._channel.send("runTo", None, {"location": location}) + await self._channel.runTo( + location={ + "file": location["file"], + "line": location.get("line"), + "column": location.get("column"), + } + ) @property def paused_details(self) -> Optional[DebuggerPausedDetails]: diff --git a/playwright/_impl/_dialog.py b/playwright/_impl/_dialog.py index f6750e396..8cc1112d5 100644 --- a/playwright/_impl/_dialog.py +++ b/playwright/_impl/_dialog.py @@ -16,13 +16,15 @@ from playwright._impl._connection import ChannelOwner, from_nullable_channel from playwright._impl._errors import is_target_closed_error -from playwright._impl._helper import locals_to_params if TYPE_CHECKING: # pragma: no cover from playwright._impl._page import Page + from playwright._impl._protocol_channels import DialogChannel class Dialog(ChannelOwner): + _channel: "DialogChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -48,15 +50,12 @@ def default_value(self) -> str: def page(self) -> Optional["Page"]: return self._page - async def accept(self, promptText: str = None) -> None: - await self._channel.send("accept", None, locals_to_params(locals())) + async def accept(self, prompt_text: Optional[str]) -> None: + await self._channel.accept(promptText=prompt_text) async def dismiss(self) -> None: try: - await self._channel.send( - "dismiss", - None, - ) + await self._channel.dismiss() except Exception as e: if is_target_closed_error(e): return diff --git a/playwright/_impl/_disposable.py b/playwright/_impl/_disposable.py index 8035c92aa..eb432e693 100644 --- a/playwright/_impl/_disposable.py +++ b/playwright/_impl/_disposable.py @@ -13,14 +13,19 @@ # limitations under the License. import traceback -from typing import Any, Awaitable, Callable, Coroutine, Dict +from typing import TYPE_CHECKING, Any, Awaitable, Callable, Coroutine, Dict from playwright._impl._connection import ChannelOwner, _capture_stack_trace from playwright._impl._errors import Error, is_target_closed_error from playwright._impl._greenlets import connection_closed_error, wait_for_future +if TYPE_CHECKING: # pragma: no cover + from playwright._impl._protocol_channels import DisposableChannel + class Disposable(ChannelOwner): + _channel: "DisposableChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -28,10 +33,7 @@ def __init__( async def dispose(self) -> None: try: - await self._channel.send( - "dispose", - None, - ) + await self._channel.dispose() except Exception as e: if not is_target_closed_error(e): raise e diff --git a/playwright/_impl/_element_handle.py b/playwright/_impl/_element_handle.py index c01dd4b44..0448ea4cc 100644 --- a/playwright/_impl/_element_handle.py +++ b/playwright/_impl/_element_handle.py @@ -34,7 +34,6 @@ KeyboardModifier, MouseButton, async_writefile, - locals_to_params, make_dirs_for_file, ) from playwright._impl._js_handle import ( @@ -48,235 +47,264 @@ if TYPE_CHECKING: # pragma: no cover from playwright._impl._frame import Frame from playwright._impl._locator import Locator + from playwright._impl._protocol_channels import ElementHandleChannel class ElementHandle(JSHandle): + _channel: "ElementHandleChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: super().__init__(parent, type, guid, initializer) self._frame = cast("Frame", parent) - async def _createSelectorForTest(self, name: str) -> Optional[str]: - return await self._channel.send( - "createSelectorForTest", self._frame._timeout, dict(name=name) - ) - def as_element(self) -> Optional["ElementHandle"]: return self async def owner_frame(self) -> Optional["Frame"]: - return from_nullable_channel(await self._channel.send("ownerFrame", None)) + return from_nullable_channel((await self._channel.ownerFrame())["frame"]) async def content_frame(self) -> Optional["Frame"]: - return from_nullable_channel(await self._channel.send("contentFrame", None)) + return from_nullable_channel((await self._channel.contentFrame())["frame"]) async def get_attribute(self, name: str) -> Optional[str]: - return await self._channel.send("getAttribute", None, dict(name=name)) + return (await self._channel.getAttribute(name=name))["value"] async def text_content(self) -> Optional[str]: - return await self._channel.send("textContent", None) + return (await self._channel.textContent())["value"] async def inner_text(self) -> str: - return await self._channel.send("innerText", None) + return (await self._channel.innerText())["value"] async def inner_html(self) -> str: - return await self._channel.send("innerHTML", None) + return (await self._channel.innerHTML())["value"] async def is_checked(self) -> bool: - return await self._channel.send("isChecked", None) + return (await self._channel.isChecked())["value"] async def is_disabled(self) -> bool: - return await self._channel.send("isDisabled", None) + return (await self._channel.isDisabled())["value"] async def is_editable(self) -> bool: - return await self._channel.send("isEditable", None) + return (await self._channel.isEditable())["value"] async def is_enabled(self) -> bool: - return await self._channel.send("isEnabled", None) + return (await self._channel.isEnabled())["value"] async def is_hidden(self) -> bool: - return await self._channel.send("isHidden", None) + return (await self._channel.isHidden())["value"] async def is_visible(self) -> bool: - return await self._channel.send("isVisible", None) + return (await self._channel.isVisible())["value"] - async def dispatch_event(self, type: str, eventInit: Dict = None) -> None: - await self._channel.send( - "dispatchEvent", - None, - dict(type=type, eventInit=serialize_argument(eventInit)), + async def dispatch_event(self, type: str, event_init: Optional[Dict]) -> None: + await self._channel.dispatchEvent( + type=type, eventInit=serialize_argument(event_init) ) - async def scroll_into_view_if_needed(self, timeout: float = None) -> None: - await self._channel.send( - "scrollIntoViewIfNeeded", self._frame._timeout, locals_to_params(locals()) + async def scroll_into_view_if_needed(self, timeout: Optional[float]) -> None: + await self._channel.scrollIntoViewIfNeeded( + timeout=self._frame._timeout(timeout) ) async def hover( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. + force: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send( - "hover", self._frame._timeout, locals_to_params(locals()) + await self._channel.hover( + force=force, + scroll=scroll, + modifiers=modifiers, + position=position, + trial=trial, + timeout=self._frame._timeout(timeout), ) async def click( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - clickCount: int = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + click_count: Optional[int], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + steps: Optional[int], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send( - "click", self._frame._timeout, locals_to_params(locals()) + await self._channel.click( + force=force, + scroll=scroll, + noWaitAfter=no_wait_after, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + clickCount=click_count, + trial=trial, + steps=steps, + timeout=self._frame._timeout(timeout), ) async def dblclick( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + trial: Optional[bool], + steps: Optional[int], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send( - "dblclick", self._frame._timeout, locals_to_params(locals()) + await self._channel.dblclick( + force=force, + scroll=scroll, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + trial=trial, + steps=steps, + timeout=self._frame._timeout(timeout), ) async def select_option( self, - value: Union[str, Sequence[str]] = None, - index: Union[int, Sequence[int]] = None, - label: Union[str, Sequence[str]] = None, - element: Union["ElementHandle", Sequence["ElementHandle"]] = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, + value: Optional[Union[str, Sequence[str]]], + index: Optional[Union[int, Sequence[int]]], + label: Optional[Union[str, Sequence[str]]], + element: Optional[Union["ElementHandle", Sequence["ElementHandle"]]], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. ) -> List[str]: - params = locals_to_params( - dict( - timeout=timeout, - force=force, - **convert_select_option_values(value, index, label, element), - ) + converted = convert_select_option_values(value, index, label, element) + result = await self._channel.selectOption( + elements=converted.get("elements"), + options=converted.get("options"), + force=force, + timeout=self._frame._timeout(timeout), ) - return await self._channel.send("selectOption", self._frame._timeout, params) + return result["values"] async def tap( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send( - "tap", self._frame._timeout, locals_to_params(locals()) + await self._channel.tap( + force=force, + scroll=scroll, + modifiers=modifiers, + position=position, + trial=trial, + timeout=self._frame._timeout(timeout), ) async def fill( self, value: str, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. + force: Optional[bool], ) -> None: - await self._channel.send( - "fill", self._frame._timeout, locals_to_params(locals()) + await self._channel.fill( + value=value, force=force, timeout=self._frame._timeout(timeout) ) - async def select_text(self, force: bool = None, timeout: float = None) -> None: - await self._channel.send( - "selectText", self._frame._timeout, locals_to_params(locals()) + async def select_text( + self, force: Optional[bool], timeout: Optional[float] + ) -> None: + await self._channel.selectText( + force=force, timeout=self._frame._timeout(timeout) ) - async def input_value(self, timeout: float = None) -> str: - return await self._channel.send( - "inputValue", self._frame._timeout, locals_to_params(locals()) - ) + async def input_value(self, timeout: Optional[float]) -> str: + result = await self._channel.inputValue(timeout=self._frame._timeout(timeout)) + return result["value"] async def set_input_files( self, files: Union[ str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload] ], - timeout: float = None, - noWaitAfter: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. ) -> None: frame = await self.owner_frame() if not frame: raise Error("Cannot set input files to detached element") converted = await convert_input_files(files, frame.page.context) - await self._channel.send( - "setInputFiles", - self._frame._timeout, - { - "timeout": timeout, - **converted, - }, + await self._channel.setInputFiles( + payloads=converted.get("payloads"), + localDirectory=converted.get("localDirectory"), + directoryStream=converted.get("directoryStream"), + localPaths=converted.get("localPaths"), + streams=converted.get("streams"), + timeout=self._frame._timeout(timeout), ) async def focus(self) -> None: - await self._channel.send("focus", None) + await self._channel.focus() async def type( self, text: str, - delay: float = None, - timeout: float = None, - noWaitAfter: bool = None, + delay: Optional[float], + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. ) -> None: - await self._channel.send( - "type", self._frame._timeout, locals_to_params(locals()) + await self._channel.type( + text=text, delay=delay, timeout=self._frame._timeout(timeout) ) async def press( self, key: str, - delay: float = None, - timeout: float = None, - noWaitAfter: bool = None, + delay: Optional[float], + timeout: Optional[float], + no_wait_after: Optional[bool], ) -> None: - await self._channel.send( - "press", self._frame._timeout, locals_to_params(locals()) + await self._channel.press( + key=key, + delay=delay, + noWaitAfter=no_wait_after, + timeout=self._frame._timeout(timeout), ) async def set_checked( self, checked: bool, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: if checked: await self.check( position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, trial=trial, scroll=scroll, ) @@ -285,91 +313,99 @@ async def set_checked( position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, trial=trial, scroll=scroll, ) async def check( self, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send( - "check", self._frame._timeout, locals_to_params(locals()) + await self._channel.check( + force=force, + scroll=scroll, + position=position, + trial=trial, + timeout=self._frame._timeout(timeout), ) async def uncheck( self, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send( - "uncheck", self._frame._timeout, locals_to_params(locals()) + await self._channel.uncheck( + force=force, + scroll=scroll, + position=position, + trial=trial, + timeout=self._frame._timeout(timeout), ) async def bounding_box(self) -> Optional[FloatRect]: - return await self._channel.send("boundingBox", None) + return (await self._channel.boundingBox())["value"] async def screenshot( self, - timeout: float = None, - type: Literal["jpeg", "png", "webp"] = None, - path: Union[str, Path] = None, - quality: int = None, - omitBackground: bool = None, - animations: Literal["allow", "disabled"] = None, - caret: Literal["hide", "initial"] = None, - scale: Literal["css", "device"] = None, - mask: Sequence["Locator"] = None, - maskColor: str = None, - style: str = None, + timeout: Optional[float], + type: Optional[Literal["jpeg", "png", "webp"]], + path: Optional[Union[str, Path]], + quality: Optional[int], + omit_background: Optional[bool], + animations: Optional[Literal["allow", "disabled"]], + caret: Optional[Literal["hide", "initial"]], + scale: Optional[Literal["css", "device"]], + mask: Optional[Sequence["Locator"]], + mask_color: Optional[str], + style: Optional[str], ) -> bytes: - params = locals_to_params(locals()) - if "path" in params: - if "type" not in params: - params["type"] = determine_screenshot_type(params["path"]) - del params["path"] - if "mask" in params: - params["mask"] = list( - map( - lambda locator: ( - { - "frame": locator._frame._channel, - "selector": locator._selector, - } - ), - params["mask"], - ) - ) - encoded_binary = await self._channel.send( - "screenshot", self._frame._timeout, params + if path is not None and type is None: + type = determine_screenshot_type(path) + result = await self._channel.screenshot( + type=type, + quality=quality, + omitBackground=omit_background, + caret=caret, + animations=animations, + scale=scale, + mask=( + [ + {"frame": locator._frame._channel, "selector": locator._selector} + for locator in mask + ] + if mask is not None + else None + ), + maskColor=mask_color, + style=style, + timeout=self._frame._timeout(timeout), ) - decoded_binary = base64.b64decode(encoded_binary) + decoded_binary = base64.b64decode(result["binary"]) if path: make_dirs_for_file(path) await async_writefile(path, decoded_binary) return decoded_binary async def query_selector(self, selector: str) -> Optional["ElementHandle"]: - return from_nullable_channel( - await self._channel.send("querySelector", None, dict(selector=selector)) - ) + result = await self._channel.querySelector(selector=selector, strict=None) + return from_nullable_channel(result["element"]) async def query_selector_all(self, selector: str) -> List["ElementHandle"]: + result = await self._channel.querySelectorAll(selector=selector) return list( map( cast(Callable[[Any], Any], from_nullable_channel), - await self._channel.send( - "querySelectorAll", None, dict(selector=selector) - ), + result["elements"], ) ) @@ -377,61 +413,56 @@ async def eval_on_selector( self, selector: str, expression: str, - arg: Serializable = None, + arg: Serializable, ) -> Any: - return parse_result( - await self._channel.send( - "evalOnSelector", - None, - dict( - selector=selector, - expression=expression, - arg=serialize_argument(arg), - ), - ) + result = await self._channel.evalOnSelector( + selector=selector, + strict=None, + expression=expression, + isFunction=None, + arg=serialize_argument(arg), ) + return parse_result(result["value"]) async def eval_on_selector_all( self, selector: str, expression: str, - arg: Serializable = None, + arg: Serializable, ) -> Any: - return parse_result( - await self._channel.send( - "evalOnSelectorAll", - None, - dict( - selector=selector, - expression=expression, - arg=serialize_argument(arg), - ), - ) + result = await self._channel.evalOnSelectorAll( + selector=selector, + expression=expression, + isFunction=None, + arg=serialize_argument(arg), ) + return parse_result(result["value"]) async def wait_for_element_state( self, state: Literal[ "disabled", "editable", "enabled", "hidden", "stable", "visible" ], - timeout: float = None, + timeout: Optional[float], ) -> None: - await self._channel.send( - "waitForElementState", self._frame._timeout, locals_to_params(locals()) + await self._channel.waitForElementState( + state=state, timeout=self._frame._timeout(timeout) ) async def wait_for_selector( self, selector: str, - state: Literal["attached", "detached", "hidden", "visible"] = None, - timeout: float = None, - strict: bool = None, + state: Optional[Literal["attached", "detached", "hidden", "visible"]], + timeout: Optional[float], + strict: Optional[bool], ) -> Optional["ElementHandle"]: - return from_nullable_channel( - await self._channel.send( - "waitForSelector", self._frame._timeout, locals_to_params(locals()) - ) + result = await self._channel.waitForSelector( + selector=selector, + strict=strict, + state=state, + timeout=self._frame._timeout(timeout), ) + return from_nullable_channel(result["element"]) def convert_select_option_values( diff --git a/playwright/_impl/_errors.py b/playwright/_impl/_errors.py index 3bc1d0127..bcf6ddd38 100644 --- a/playwright/_impl/_errors.py +++ b/playwright/_impl/_errors.py @@ -21,7 +21,10 @@ import re import traceback from types import TracebackType -from typing import Any, List, Optional, TypedDict +from typing import TYPE_CHECKING, Any, List, Optional + +if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels def is_target_closed_error(error: Exception) -> bool: @@ -69,20 +72,19 @@ def rewrite_error(error: Exception, message: str) -> Exception: return rewritten_exc -class ErrorPayload(TypedDict, total=False): - message: str - name: str - stack: str - value: Optional[Any] - - -def serialize_error(ex: Exception, tb: Optional[TracebackType]) -> ErrorPayload: - return ErrorPayload( - message=str(ex), name="Error", stack="".join(traceback.format_tb(tb)) - ) +def serialize_error( + ex: Exception, tb: Optional[TracebackType] +) -> "channels.SerializedErrorError": + return { + "message": str(ex), + "name": "Error", + "stack": "".join(traceback.format_tb(tb)), + } -def parse_error(error: ErrorPayload, log: Optional[str] = None) -> Error: +def parse_error( + error: "channels.SerializedErrorError", log: Optional[str] = None +) -> Error: base_error_class = Error if error.get("name") == "TimeoutError": base_error_class = TimeoutError @@ -92,7 +94,7 @@ def parse_error(error: ErrorPayload, log: Optional[str] = None) -> Error: log = "" exc = base_error_class(patch_error_message(error["message"]) + log) exc._name = error["name"] - exc._stack = error["stack"] + exc._stack = error.get("stack") return exc diff --git a/playwright/_impl/_fetch.py b/playwright/_impl/_fetch.py index 1d7fd53ea..072e7a110 100644 --- a/playwright/_impl/_fetch.py +++ b/playwright/_impl/_fetch.py @@ -41,25 +41,21 @@ async_readfile, async_writefile, is_file_payload, - locals_to_params, to_impl, ) -from playwright._impl._internal_structures import ( - FormField, - Headers, - NameValue, - ServerFilePayload, -) +from playwright._impl._internal_structures import Headers, NameValue from playwright._impl._network import ( - to_clear_cookies_params, to_client_certificates_protocol, to_http_credentials_protocol, ) +from playwright._impl._str_utils import escape_regex_flags from playwright._impl._timeout_settings import TimeoutSettings from playwright._impl._tracing import Tracing -if typing.TYPE_CHECKING: +if typing.TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._playwright import Playwright + from playwright._impl._protocol_channels import APIRequestContextChannel FormType = Union[Dict[str, Union[bool, float, str]], FormData] @@ -76,45 +72,54 @@ def __init__(self, playwright: "Playwright") -> None: async def new_context( self, - baseURL: str = None, - extraHTTPHeaders: Dict[str, str] = None, - httpCredentials: Union[HttpCredentials, Sequence[HttpCredentials]] = None, - ignoreHTTPSErrors: bool = None, - proxy: ProxySettings = None, - userAgent: str = None, - timeout: float = None, - storageState: Union[StorageState, str, Path] = None, - clientCertificates: List[ClientCertificate] = None, - failOnStatusCode: bool = None, - maxRedirects: int = None, + base_url: Optional[str], + extra_http_headers: Optional[Dict[str, str]], + http_credentials: Optional[Union[HttpCredentials, Sequence[HttpCredentials]]], + ignore_https_errors: Optional[bool], + proxy: Optional[ProxySettings], + user_agent: Optional[str], + timeout: Optional[float], + storage_state: Optional[Union[StorageState, str, Path]], + client_certificates: Optional[List[ClientCertificate]], + fail_on_status_code: Optional[bool], + max_redirects: Optional[int], ) -> "APIRequestContext": - params = locals_to_params(locals()) - if "storageState" in params: - storage_state = params["storageState"] - if not isinstance(storage_state, dict) and storage_state: - params["storageState"] = json.loads( - (await async_readfile(storage_state)).decode() - ) - if "extraHTTPHeaders" in params: - params["extraHTTPHeaders"] = serialize_headers(params["extraHTTPHeaders"]) - params["clientCertificates"] = await to_client_certificates_protocol( - params.get("clientCertificates") - ) - if "httpCredentials" in params: - params["httpCredentials"] = to_http_credentials_protocol( - params["httpCredentials"] - ) - context = cast( - APIRequestContext, - from_channel( - await self.playwright._channel.send("newRequest", None, params) + if storage_state and not isinstance(storage_state, dict): + storage_state = json.loads((await async_readfile(storage_state)).decode()) + result = await self.playwright._channel.newRequest( + baseURL=base_url, + userAgent=user_agent, + ignoreHTTPSErrors=ignore_https_errors, + extraHTTPHeaders=( + serialize_headers(extra_http_headers) + if extra_http_headers is not None + else None + ), + failOnStatusCode=fail_on_status_code, + clientCertificates=cast( + "Optional[List[channels.PlaywrightNewRequestParamsClientCertificates]]", + await to_client_certificates_protocol(client_certificates), + ), + maxRedirects=max_redirects, + httpCredentials=cast( + "Optional[List[channels.HttpCredentials]]", + to_http_credentials_protocol(http_credentials), ), + proxy=cast("Optional[channels.PlaywrightNewRequestParamsProxy]", proxy), + storageState=cast( + "Optional[channels.PlaywrightNewRequestParamsStorageState]", + storage_state, + ), + tracesDir=None, ) + context = cast(APIRequestContext, from_channel(result["request"])) context._timeout_settings.set_default_timeout(timeout) return context class APIRequestContext(ChannelOwner): + _channel: "APIRequestContextChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -123,11 +128,11 @@ def __init__( self._close_reason: Optional[str] = None self._timeout_settings = TimeoutSettings(None) - async def dispose(self, reason: str = None) -> None: + async def dispose(self, reason: Optional[str]) -> None: self._close_reason = reason await self._tracing._export_all_hars() try: - await self._channel.send("dispose", None, {"reason": reason}) + await self._channel.dispose(reason=reason) except Error as e: if is_target_closed_error(e): return @@ -138,254 +143,270 @@ async def dispose(self, reason: str = None) -> None: def tracing(self) -> Tracing: return self._tracing - async def cookies(self, urls: Union[str, Sequence[str]] = None) -> List[Cookie]: + async def cookies(self, urls: Optional[Union[str, Sequence[str]]]) -> List[Cookie]: if urls is None: urls = [] if isinstance(urls, str): urls = [urls] - return await self._channel.send("cookies", None, dict(urls=urls)) + return cast(List[Cookie], (await self._channel.cookies(urls=urls))["cookies"]) async def add_cookies(self, cookies: Sequence[SetCookieParam]) -> None: - await self._channel.send("addCookies", None, dict(cookies=cookies)) + await self._channel.addCookies( + cookies=cast("Sequence[channels.SetNetworkCookie]", cookies) + ) async def clear_cookies( self, - name: Union[str, Pattern[str]] = None, - domain: Union[str, Pattern[str]] = None, - path: Union[str, Pattern[str]] = None, + name: Optional[Union[str, Pattern[str]]], + domain: Optional[Union[str, Pattern[str]]], + path: Optional[Union[str, Pattern[str]]], ) -> None: - await self._channel.send( - "clearCookies", None, to_clear_cookies_params(name, domain, path) + await self._channel.clearCookies( + name=name if isinstance(name, str) else None, + nameRegexSource=name.pattern if isinstance(name, Pattern) else None, + nameRegexFlags=( + escape_regex_flags(name) if isinstance(name, Pattern) else None + ), + domain=domain if isinstance(domain, str) else None, + domainRegexSource=domain.pattern if isinstance(domain, Pattern) else None, + domainRegexFlags=( + escape_regex_flags(domain) if isinstance(domain, Pattern) else None + ), + path=path if isinstance(path, str) else None, + pathRegexSource=path.pattern if isinstance(path, Pattern) else None, + pathRegexFlags=( + escape_regex_flags(path) if isinstance(path, Pattern) else None + ), ) async def delete( self, url: str, - params: ParamsType = None, - headers: Headers = None, - data: DataType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + params: Optional[ParamsType], + headers: Optional[Headers], + data: Optional[DataType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": return await self.fetch( - url, - method="DELETE", + url_or_request=url, params=params, + method="DELETE", headers=headers, data=data, form=form, multipart=multipart, timeout=timeout, - failOnStatusCode=failOnStatusCode, - ignoreHTTPSErrors=ignoreHTTPSErrors, - maxRedirects=maxRedirects, - maxRetries=maxRetries, + fail_on_status_code=fail_on_status_code, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, ) async def head( self, url: str, - params: ParamsType = None, - headers: Headers = None, - data: DataType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + params: Optional[ParamsType], + headers: Optional[Headers], + data: Optional[DataType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": return await self.fetch( - url, - method="HEAD", + url_or_request=url, params=params, + method="HEAD", headers=headers, data=data, form=form, multipart=multipart, timeout=timeout, - failOnStatusCode=failOnStatusCode, - ignoreHTTPSErrors=ignoreHTTPSErrors, - maxRedirects=maxRedirects, - maxRetries=maxRetries, + fail_on_status_code=fail_on_status_code, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, ) async def get( self, url: str, - params: ParamsType = None, - headers: Headers = None, - data: DataType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + params: Optional[ParamsType], + headers: Optional[Headers], + data: Optional[DataType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": return await self.fetch( - url, - method="GET", + url_or_request=url, params=params, + method="GET", headers=headers, data=data, form=form, multipart=multipart, timeout=timeout, - failOnStatusCode=failOnStatusCode, - ignoreHTTPSErrors=ignoreHTTPSErrors, - maxRedirects=maxRedirects, - maxRetries=maxRetries, + fail_on_status_code=fail_on_status_code, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, ) async def patch( self, url: str, - params: ParamsType = None, - headers: Headers = None, - data: DataType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + params: Optional[ParamsType], + headers: Optional[Headers], + data: Optional[DataType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": return await self.fetch( - url, - method="PATCH", + url_or_request=url, params=params, + method="PATCH", headers=headers, data=data, form=form, multipart=multipart, timeout=timeout, - failOnStatusCode=failOnStatusCode, - ignoreHTTPSErrors=ignoreHTTPSErrors, - maxRedirects=maxRedirects, - maxRetries=maxRetries, + fail_on_status_code=fail_on_status_code, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, ) async def put( self, url: str, - params: ParamsType = None, - headers: Headers = None, - data: DataType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + params: Optional[ParamsType], + headers: Optional[Headers], + data: Optional[DataType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": return await self.fetch( - url, - method="PUT", + url_or_request=url, params=params, + method="PUT", headers=headers, data=data, form=form, multipart=multipart, timeout=timeout, - failOnStatusCode=failOnStatusCode, - ignoreHTTPSErrors=ignoreHTTPSErrors, - maxRedirects=maxRedirects, - maxRetries=maxRetries, + fail_on_status_code=fail_on_status_code, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, ) async def post( self, url: str, - params: ParamsType = None, - headers: Headers = None, - data: DataType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + params: Optional[ParamsType], + headers: Optional[Headers], + data: Optional[DataType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": return await self.fetch( - url, - method="POST", + url_or_request=url, params=params, + method="POST", headers=headers, data=data, form=form, multipart=multipart, timeout=timeout, - failOnStatusCode=failOnStatusCode, - ignoreHTTPSErrors=ignoreHTTPSErrors, - maxRedirects=maxRedirects, - maxRetries=maxRetries, + fail_on_status_code=fail_on_status_code, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, ) async def fetch( self, - urlOrRequest: Union[str, network.Request], - params: ParamsType = None, - method: str = None, - headers: Headers = None, - data: DataType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + url_or_request: Union[str, network.Request], + params: Optional[ParamsType], + method: Optional[str], + headers: Optional[Headers], + data: Optional[DataType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": - url = urlOrRequest if isinstance(urlOrRequest, str) else None + url = url_or_request if isinstance(url_or_request, str) else None request = ( - cast(network.Request, to_impl(urlOrRequest)) - if isinstance(to_impl(urlOrRequest), network.Request) + cast(network.Request, to_impl(url_or_request)) + if isinstance(to_impl(url_or_request), network.Request) else None ) - if not request and not isinstance(urlOrRequest, str): + if not request and not isinstance(url_or_request, str): raise Error("First argument must be either URL string or Request") return await self._inner_fetch( - request, - url, - method, - headers, - data, - params, - form, - multipart, - timeout, - failOnStatusCode, - ignoreHTTPSErrors, - maxRedirects, - maxRetries, + request=request, + url=url, + method=method, + headers=headers, + data=data, + params=params, + form=form, + multipart=multipart, + timeout=timeout, + fail_on_status_code=fail_on_status_code, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, ) async def _inner_fetch( self, request: Optional[network.Request], url: Optional[str], - method: str = None, - headers: Headers = None, - data: DataType = None, - params: ParamsType = None, - form: FormType = None, - multipart: MultipartType = None, - timeout: float = None, - failOnStatusCode: bool = None, - ignoreHTTPSErrors: bool = None, - maxRedirects: int = None, - maxRetries: int = None, + method: Optional[str], + headers: Optional[Headers], + data: Optional[DataType], + params: Optional[ParamsType], + form: Optional[FormType], + multipart: Optional[MultipartType], + timeout: Optional[float], + fail_on_status_code: Optional[bool], + ignore_https_errors: Optional[bool], + max_redirects: Optional[int], + max_retries: Optional[int], ) -> "APIResponse": if self._close_reason: raise TargetClosedError(self._close_reason) @@ -395,18 +416,20 @@ async def _inner_fetch( + (1 if multipart is not None else 0) ) > 1: raise Error("Only one of 'data', 'form' or 'multipart' can be specified") - if maxRedirects is not None and maxRedirects < 0: + if max_redirects is not None and max_redirects < 0: raise Error("'max_redirects' must be greater than or equal to '0'") - if maxRetries is not None and maxRetries < 0: + if max_retries is not None and max_retries < 0: raise Error("'max_retries' must be greater than or equal to '0'") url = url or (request.url if request else url) + if url is None: + raise Error("url: expected a URL string or a Request") method = method or (request.method if request else "GET") # Cannot call allHeaders() here as the request may be paused inside route handler. headers_obj = headers or (request.headers if request else None) serialized_headers = serialize_headers(headers_obj) if headers_obj else None json_data: Any = None form_data: Optional[List[NameValue]] = None - multipart_data: Optional[List[FormField]] = None + multipart_data: Optional[List[channels.FormField]] = None post_data_buffer: Optional[bytes] = None if data is not None: if isinstance(data, str): @@ -458,36 +481,33 @@ async def _inner_fetch( base64.b64encode(post_data_buffer).decode() if post_data_buffer else None ) - response = await self._channel.send( - "fetch", - self._timeout_settings.timeout, - { - "url": url, - "timeout": timeout, - "params": object_to_array(params) if isinstance(params, dict) else None, - "encodedParams": params if isinstance(params, str) else None, - "method": method, - "headers": serialized_headers, - "postData": post_data, - "jsonData": json_data, - "formData": form_data, - "multipartData": multipart_data, - "failOnStatusCode": failOnStatusCode, - "ignoreHTTPSErrors": ignoreHTTPSErrors, - "maxRedirects": maxRedirects, - "maxRetries": maxRetries, - }, + result = await self._channel.fetch( + url=url, + encodedParams=params if isinstance(params, str) else None, + params=object_to_array(params) if isinstance(params, dict) else None, + method=method, + headers=serialized_headers, + postData=post_data, + jsonData=json_data, + formData=form_data, + multipartData=multipart_data, + failOnStatusCode=fail_on_status_code, + ignoreHTTPSErrors=ignore_https_errors, + maxRedirects=max_redirects, + maxRetries=max_retries, + timeout=self._timeout_settings.timeout(timeout), ) - return APIResponse(self, response) + return APIResponse(self, cast(Dict, result["response"])) async def storage_state( self, - path: Union[pathlib.Path, str] = None, - indexedDB: bool = None, - opfs: bool = None, + path: Optional[Union[pathlib.Path, str]], + indexed_db: Optional[bool], + opfs: Optional[bool], ) -> StorageState: - result = await self._channel.send_return_as_dict( - "storageState", None, {"indexedDB": indexedDB, "opfs": opfs} + result = cast( + StorageState, + await self._channel.storageState(indexedDB=indexed_db, opfs=opfs), ) if path: await async_writefile(path, json.dumps(result)) @@ -503,32 +523,32 @@ def object_to_array(obj: Optional[Dict]) -> Optional[List[NameValue]]: return result -def file_payload_to_json(payload: FilePayload) -> ServerFilePayload: - return ServerFilePayload( - name=payload["name"], - mimeType=payload["mimeType"], - buffer=base64.b64encode(payload["buffer"]).decode(), - ) +def file_payload_to_json(payload: FilePayload) -> "channels.FormFieldFile": + return { + "name": payload["name"], + "mimeType": payload["mimeType"], + "buffer": base64.b64encode(payload["buffer"]).decode(), + } -async def _form_data_field_to_form_field(name: str, value: Any) -> FormField: +async def _form_data_field_to_form_field(name: str, value: Any) -> "channels.FormField": if isinstance(value, pathlib.Path): mime_type, _ = mimetypes.guess_type(str(value)) - return FormField( - name=name, - file=ServerFilePayload( - name=value.name, - mimeType=mime_type or "application/octet-stream", - buffer=base64.b64encode(await async_readfile(str(value))).decode(), - ), - ) + return { + "name": name, + "file": { + "name": value.name, + "mimeType": mime_type or "application/octet-stream", + "buffer": base64.b64encode(await async_readfile(str(value))).decode(), + }, + } if is_file_payload(value): payload = cast(FilePayload, value) if not isinstance(payload["buffer"], bytes): raise Error(f"Unexpected buffer type of form field {name!r}") - return FormField(name=name, file=file_payload_to_json(payload)) + return {"name": name, "file": file_payload_to_json(payload)} if isinstance(value, (str, int, float, bool)): - return FormField(name=name, value=str(value)) + return {"name": name, "value": str(value)} raise Error(f"Unsupported form field {name!r} value type: {type(value).__name__}") @@ -588,16 +608,12 @@ def timing(self) -> ResourceTiming: async def body(self) -> bytes: try: result = await self._request._connection.wrap_api_call( - lambda: self._request._channel.send_return_as_dict( - "fetchResponseBody", - None, - { - "fetchUid": self._fetch_uid, - }, + lambda: self._request._channel.fetchResponseBody( + fetchUid=self._fetch_uid ), - True, + is_internal=True, ) - if result is None: + if result["binary"] is None: raise Error("Response has been disposed") return base64.b64decode(result["binary"]) except Error as exc: @@ -620,26 +636,14 @@ async def server_addr(self) -> Optional[RemoteAddr]: return self._initializer.get("serverAddr") or None async def dispose(self) -> None: - await self._request._channel.send( - "disposeAPIResponse", - None, - { - "fetchUid": self._fetch_uid, - }, - ) + await self._request._channel.disposeAPIResponse(fetchUid=self._fetch_uid) @property def _fetch_uid(self) -> str: return self._initializer["fetchUid"] async def _fetch_log(self) -> List[str]: - return await self._request._channel.send( - "fetchLog", - None, - { - "fetchUid": self._fetch_uid, - }, - ) + return (await self._request._channel.fetchLog(fetchUid=self._fetch_uid))["log"] def is_json_content_type(headers: network.HeadersArray = None) -> bool: diff --git a/playwright/_impl/_file_chooser.py b/playwright/_impl/_file_chooser.py index 951919d22..efddfcec6 100644 --- a/playwright/_impl/_file_chooser.py +++ b/playwright/_impl/_file_chooser.py @@ -13,7 +13,7 @@ # limitations under the License. from pathlib import Path -from typing import TYPE_CHECKING, Sequence, Union +from typing import TYPE_CHECKING, Optional, Sequence, Union from playwright._impl._api_structures import FilePayload @@ -51,7 +51,9 @@ async def set_files( files: Union[ str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload] ], - timeout: float = None, - noWaitAfter: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], ) -> None: - await self._element_handle.set_input_files(files, timeout, noWaitAfter) + await self._element_handle.set_input_files( + files=files, timeout=timeout, no_wait_after=no_wait_after + ) diff --git a/playwright/_impl/_frame.py b/playwright/_impl/_frame.py index ab554ca41..b601b8cce 100644 --- a/playwright/_impl/_frame.py +++ b/playwright/_impl/_frame.py @@ -41,12 +41,10 @@ from playwright._impl._event_context_manager import EventContextManagerImpl from playwright._impl._helper import ( DocumentLoadState, - FrameNavigatedEvent, KeyboardModifier, MouseButton, URLMatch, async_readfile, - locals_to_params, url_matches, ) from playwright._impl._internal_structures import ( @@ -85,10 +83,14 @@ from playwright._impl._web_mcp import WebMCP if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._page import Page + from playwright._impl._protocol_channels import FrameChannel, JSHandleChannel class Frame(ChannelOwner): + _channel: "FrameChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -104,14 +106,10 @@ def __init__( self._load_states: Set[str] = set(initializer["loadStates"]) self._event_emitter = EventEmitter() self._webmcp = WebMCP(self) - self._channel.on( - "loadstate", - lambda params: self._on_load_state(params.get("add"), params.get("remove")), - ) - self._channel.on( - "navigated", - lambda params: self._on_frame_navigated(params), + self._channel.onLoadstate( + lambda event: self._on_load_state(event["add"], event["remove"]) ) + self._channel.onNavigated(self._on_frame_navigated) def __repr__(self) -> str: return f"" @@ -130,16 +128,16 @@ def _on_load_state( if not self._parent_frame and add == "domcontentloaded" and self._page: self._page.emit("domcontentloaded", self._page) - def _on_frame_navigated(self, event: FrameNavigatedEvent) -> None: + def _on_frame_navigated(self, event: "channels.FrameNavigatedEvent") -> None: self._url = event["url"] self._name = event["name"] self._event_emitter.emit("navigated", event) - if "error" not in event and self._page: + if not event["error"] and self._page: self._page.emit("framenavigated", self) self._page.context.emit("framenavigated", self) async def _query_count(self, selector: str) -> int: - return await self._channel.send("queryCount", None, {"selector": selector}) + return (await self._channel.queryCount(selector=selector))["value"] @property def page(self) -> "Page": @@ -149,18 +147,17 @@ def page(self) -> "Page": async def goto( self, url: str, - timeout: float = None, - waitUntil: DocumentLoadState = None, - referer: str = None, + timeout: Optional[float], + wait_until: Optional[DocumentLoadState], + referer: Optional[str], ) -> Optional[Response]: - return cast( - Optional[Response], - from_nullable_channel( - await self._channel.send( - "goto", self._navigation_timeout, locals_to_params(locals()) - ) - ), + result = await self._channel.goto( + url=url, + waitUntil=wait_until, + referer=referer, + timeout=self._navigation_timeout(timeout), ) + return from_nullable_channel(result["response"]) def _setup_navigation_waiter(self, wait_name: str, timeout: float = None) -> Waiter: assert self._page @@ -190,17 +187,24 @@ async def _expect( options: FrameExpectOptions, title: str = None, ) -> FrameExpectResult: - if "expectedValue" in options: - options["expectedValue"] = serialize_argument(options["expectedValue"]) try: - await self._channel.send( - "expect", - self._timeout, - { - "selector": selector, - "expression": expression, - **options, - }, + await self._connection.wrap_api_call( + lambda: self._channel.expect( + selector=selector, + expression=expression, + expressionArg=options.get("expressionArg"), + pseudo=options.get("pseudo"), + expectedText=options.get("expectedText"), + expectedNumber=options.get("expectedNumber"), + expectedValue=( + serialize_argument(options["expectedValue"]) + if "expectedValue" in options + else None + ), + useInnerText=options.get("useInnerText"), + isNot=bool(options.get("isNot")), + timeout=self._timeout(options.get("timeout")), + ), title=title, ) return {"matches": not options.get("isNot")} @@ -230,13 +234,13 @@ async def _expect( def expect_navigation( self, - url: URLMatch = None, - waitUntil: DocumentLoadState = None, - timeout: float = None, + url: Optional[URLMatch], + wait_until: Optional[DocumentLoadState], + timeout: Optional[float], ) -> EventContextManagerImpl[Response]: assert self._page - if not waitUntil: - waitUntil = "load" + if not wait_until: + wait_until = "load" if timeout is None: timeout = self._page._timeout_settings.navigation_timeout() @@ -244,11 +248,11 @@ def expect_navigation( waiter = self._setup_navigation_waiter("expect_navigation", timeout) to_url = f' to "{url}"' if url else "" - waiter.log(f"waiting for navigation{to_url} until '{waitUntil}'") + waiter.log(f"waiting for navigation{to_url} until '{wait_until}'") - def predicate(event: Any) -> bool: + def predicate(event: "channels.FrameNavigatedEvent") -> bool: # Any failed navigation results in a rejection. - if event.get("error"): + if event["error"]: return True waiter.log(f' navigated to "{event["url"]}"') return url_matches( @@ -264,15 +268,17 @@ def predicate(event: Any) -> bool: ) async def continuation() -> Optional[Response]: - event = await waiter.result() - if "error" in event: - raise Error(event["error"]) - if waitUntil not in self._load_states: + event: "channels.FrameNavigatedEvent" = await waiter.result() + error = event["error"] + if error: + raise Error(error) + if wait_until not in self._load_states: t = deadline - monotonic_time() if t > 0: - await self._wait_for_load_state_impl(state=waitUntil, timeout=t) - if "newDocument" in event and "request" in event["newDocument"]: - request = from_channel(event["newDocument"]["request"]) + await self._wait_for_load_state_impl(state=wait_until, timeout=t) + new_document = event["newDocument"] + if new_document and "request" in new_document: + request = from_channel(new_document["request"]) return await request.response() return None @@ -281,27 +287,27 @@ async def continuation() -> Optional[Response]: async def wait_for_url( self, url: URLMatch, - waitUntil: DocumentLoadState = None, - timeout: float = None, + wait_until: Optional[DocumentLoadState], + timeout: Optional[float], ) -> None: assert self._page if url_matches(self._page._browser_context._base_url, self.url, url): - await self._wait_for_load_state_impl(state=waitUntil, timeout=timeout) + await self._wait_for_load_state_impl(state=wait_until, timeout=timeout) return async with self.expect_navigation( - url=url, waitUntil=waitUntil, timeout=timeout + url=url, wait_until=wait_until, timeout=timeout ): pass async def wait_for_load_state( self, - state: Literal["domcontentloaded", "load", "networkidle"] = None, - timeout: float = None, + state: Optional[Literal["domcontentloaded", "load", "networkidle"]], + timeout: Optional[float], ) -> None: - return await self._wait_for_load_state_impl(state, timeout) + return await self._wait_for_load_state_impl(state=state, timeout=timeout) async def _wait_for_load_state_impl( - self, state: DocumentLoadState = None, timeout: float = None + self, state: Optional[DocumentLoadState], timeout: Optional[float] ) -> None: if not state: state = "load" @@ -341,175 +347,174 @@ def _navigation_timeout(self, timeout: Optional[float]) -> float: return timeout_settings.navigation_timeout(timeout) async def frame_element(self) -> ElementHandle: - return from_channel(await self._channel.send("frameElement", None)) - - async def evaluate(self, expression: str, arg: Serializable = None) -> Any: - return parse_result( - await self._channel.send( - "evaluateExpression", - None, - dict( - expression=expression, - arg=serialize_argument(arg), - ), - ) + return from_channel((await self._channel.frameElement())["element"]) + + async def evaluate(self, expression: str, arg: Serializable) -> Any: + result = await self._channel.evaluateExpression( + expression=expression, + isFunction=None, + arg=serialize_argument(arg), ) + return parse_result(result["value"]) - async def evaluate_handle( - self, expression: str, arg: Serializable = None - ) -> JSHandle: - return from_channel( - await self._channel.send( - "evaluateExpressionHandle", - None, - dict( - expression=expression, - arg=serialize_argument(arg), - ), - ) + async def evaluate_handle(self, expression: str, arg: Serializable) -> JSHandle: + result = await self._channel.evaluateExpressionHandle( + expression=expression, + isFunction=None, + arg=serialize_argument(arg), ) + return from_channel(result["handle"]) async def query_selector( - self, selector: str, strict: bool = None + self, selector: str, strict: Optional[bool] ) -> Optional[ElementHandle]: - return from_nullable_channel( - await self._channel.send("querySelector", None, locals_to_params(locals())) - ) + result = await self._channel.querySelector(selector=selector, strict=strict) + return from_nullable_channel(result["element"]) async def query_selector_all(self, selector: str) -> List[ElementHandle]: - return list( - map( - from_channel, - await self._channel.send( - "querySelectorAll", None, dict(selector=selector) - ), - ) - ) + result = await self._channel.querySelectorAll(selector=selector) + return list(map(from_channel, result["elements"])) async def wait_for_selector( self, selector: str, - strict: bool = None, - timeout: float = None, - state: Literal["attached", "detached", "hidden", "visible"] = None, + strict: Optional[bool], + timeout: Optional[float], + state: Optional[Literal["attached", "detached", "hidden", "visible"]], + omit_return_value: Optional[bool] = None, # Not in the docs; Locator passes it. ) -> Optional[ElementHandle]: - return from_nullable_channel( - await self._channel.send( - "waitForSelector", self._timeout, locals_to_params(locals()) - ) + result = await self._channel.waitForSelector( + selector=selector, + strict=strict, + state=state, + omitReturnValue=omit_return_value, + timeout=self._timeout(timeout), ) + return from_nullable_channel(result["element"]) async def is_checked( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._channel.send( - "isChecked", self._timeout, locals_to_params(locals()) + result = await self._channel.isChecked( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] async def is_disabled( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._channel.send( - "isDisabled", self._timeout, locals_to_params(locals()) + result = await self._channel.isDisabled( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] async def is_editable( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._channel.send( - "isEditable", self._timeout, locals_to_params(locals()) + result = await self._channel.isEditable( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] async def is_enabled( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._channel.send( - "isEnabled", self._timeout, locals_to_params(locals()) + result = await self._channel.isEnabled( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] - async def is_hidden(self, selector: str, strict: bool = None) -> bool: - return await self._channel.send( - "isHidden", self._timeout, locals_to_params(locals()) + async def is_hidden( + self, + selector: str, + strict: Optional[bool], + ) -> bool: + result = await self._channel.isHidden( + selector=selector, strict=strict, timeout=self._timeout(None) ) + return result["value"] - async def is_visible(self, selector: str, strict: bool = None) -> bool: - return await self._channel.send( - "isVisible", self._timeout, locals_to_params(locals()) + async def is_visible( + self, + selector: str, + strict: Optional[bool], + ) -> bool: + result = await self._channel.isVisible( + selector=selector, strict=strict, timeout=self._timeout(None) ) + return result["value"] async def dispatch_event( self, selector: str, type: str, - eventInit: Dict = None, - strict: bool = None, - timeout: float = None, + event_init: Optional[Dict], + strict: Optional[bool], + timeout: Optional[float], ) -> None: - await self._channel.send( - "dispatchEvent", - self._timeout, - locals_to_params( - dict( - selector=selector, - type=type, - eventInit=serialize_argument(eventInit), - strict=strict, - timeout=timeout, - ), - ), + await self._channel.dispatchEvent( + selector=selector, + strict=strict, + type=type, + eventInit=serialize_argument(event_init), + timeout=self._timeout(timeout), ) async def eval_on_selector( self, selector: str, expression: str, - arg: Serializable = None, - strict: bool = None, + arg: Serializable, + strict: Optional[bool], ) -> Any: - return parse_result( - await self._channel.send( - "evalOnSelector", - None, - locals_to_params( - dict( - selector=selector, - expression=expression, - arg=serialize_argument(arg), - strict=strict, - ) - ), - ) + result = await self._channel.evalOnSelector( + selector=selector, + strict=strict, + expression=expression, + isFunction=None, + arg=serialize_argument(arg), ) + return parse_result(result["value"]) async def eval_on_selector_all( self, selector: str, expression: str, - arg: Serializable = None, + arg: Serializable, ) -> Any: - return parse_result( - await self._channel.send( - "evalOnSelectorAll", - None, - dict( - selector=selector, - expression=expression, - arg=serialize_argument(arg), - ), - ) + result = await self._channel.evalOnSelectorAll( + selector=selector, + expression=expression, + isFunction=None, + arg=serialize_argument(arg), ) + return parse_result(result["value"]) - async def content(self, includeShadow: bool = None) -> str: - return await self._channel.send("content", None, locals_to_params(locals())) + async def content(self, include_shadow: Optional[bool]) -> str: + return (await self._channel.content(includeShadow=include_shadow))["value"] async def set_content( self, html: str, - timeout: float = None, - waitUntil: DocumentLoadState = None, + timeout: Optional[float], + wait_until: Optional[DocumentLoadState], ) -> None: - await self._channel.send( - "setContent", self._navigation_timeout, locals_to_params(locals()) + await self._channel.setContent( + html=html, + waitUntil=wait_until, + timeout=self._navigation_timeout(timeout), ) @property @@ -537,300 +542,420 @@ def is_detached(self) -> bool: async def add_script_tag( self, - url: str = None, - path: Union[str, Path] = None, - content: str = None, - type: str = None, + url: Optional[str], + path: Optional[Union[str, Path]], + content: Optional[str], + type: Optional[str], ) -> ElementHandle: - params = locals_to_params(locals()) if path: - params["content"] = add_source_url_to_script( + content = add_source_url_to_script( (await async_readfile(path)).decode(), path ) - del params["path"] - return from_channel(await self._channel.send("addScriptTag", None, params)) + result = await self._channel.addScriptTag(url=url, content=content, type=type) + return from_channel(result["element"]) async def add_style_tag( - self, url: str = None, path: Union[str, Path] = None, content: str = None + self, + url: Optional[str], + path: Optional[Union[str, Path]], + content: Optional[str], ) -> ElementHandle: - params = locals_to_params(locals()) if path: - params["content"] = ( + content = ( (await async_readfile(path)).decode() + "\n/*# sourceURL=" + str(Path(path)) + "*/" ) - del params["path"] - return from_channel(await self._channel.send("addStyleTag", None, params)) + result = await self._channel.addStyleTag(url=url, content=content) + return from_channel(result["element"]) async def click( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - clickCount: int = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + click_count: Optional[int], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + steps: Optional[int] = None, # Not in the docs; Locator passes it. ) -> None: - await self._click(**locals_to_params(locals())) - - async def _click( - self, - selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - clickCount: int = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, - ) -> None: - await self._channel.send("click", self._timeout, locals_to_params(locals())) + await self._channel.click( + selector=selector, + strict=strict, + force=force, + scroll=scroll, + noWaitAfter=no_wait_after, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + clickCount=click_count, + trial=trial, + steps=steps, + timeout=self._timeout(timeout), + ) async def dblclick( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + steps: Optional[int] = None, # Not in the docs; Locator passes it. ) -> None: - await self._channel.send( - "dblclick", self._timeout, locals_to_params(locals()), title="Double click" + await self._connection.wrap_api_call( + lambda: self._channel.dblclick( + selector=selector, + strict=strict, + force=force, + scroll=scroll, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + trial=trial, + steps=steps, + timeout=self._timeout(timeout), + ), + title="Double click", ) async def tap( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send("tap", self._timeout, locals_to_params(locals())) + await self._channel.tap( + selector=selector, + strict=strict, + force=force, + scroll=scroll, + modifiers=modifiers, + position=position, + trial=trial, + timeout=self._timeout(timeout), + ) async def fill( self, selector: str, value: str, - timeout: float = None, - noWaitAfter: bool = None, - strict: bool = None, - force: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + force: Optional[bool], ) -> None: - await self._fill(**locals_to_params(locals())) + await self._fill( + selector, + value, + title=None, + timeout=timeout, + no_wait_after=no_wait_after, + strict=strict, + force=force, + ) async def _fill( self, selector: str, value: str, - timeout: float = None, - noWaitAfter: bool = None, - strict: bool = None, - force: bool = None, - title: str = None, + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + force: Optional[bool], + title: Optional[str], ) -> None: - params = locals_to_params(locals()) - params.pop("title", None) - await self._channel.send("fill", self._timeout, params, title=title) + await self._connection.wrap_api_call( + lambda: self._channel.fill( + selector=selector, + strict=strict, + value=value, + force=force, + timeout=self._timeout(timeout), + ), + title=title, + ) def locator( self, selector: str, - hasText: Union[str, Pattern[str]] = None, - hasNotText: Union[str, Pattern[str]] = None, - has: Locator = None, - hasNot: Locator = None, + has_text: Optional[Union[str, Pattern[str]]], + has_not_text: Optional[Union[str, Pattern[str]]], + has: Optional[Locator], + has_not: Optional[Locator], ) -> Locator: return Locator( self, selector, - has_text=hasText, - has_not_text=hasNotText, + has_text=has_text, + has_not_text=has_not_text, has=has, - has_not=hasNot, + has_not=has_not, ) def get_by_alt_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_alt_text_selector(text, exact=exact)) + return self.locator( + selector=get_by_alt_text_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_label( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_label_selector(text, exact=exact)) + return self.locator( + selector=get_by_label_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_placeholder( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_placeholder_selector(text, exact=exact)) + return self.locator( + selector=get_by_placeholder_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_role( self, role: AriaRole, - checked: bool = None, - disabled: bool = None, - expanded: bool = None, - includeHidden: bool = None, - level: int = None, - name: Union[str, Pattern[str]] = None, - pressed: bool = None, - selected: bool = None, - exact: bool = None, - description: Union[str, Pattern[str]] = None, + checked: Optional[bool], + disabled: Optional[bool], + expanded: Optional[bool], + include_hidden: Optional[bool], + level: Optional[int], + name: Optional[Union[str, Pattern[str]]], + pressed: Optional[bool], + selected: Optional[bool], + exact: Optional[bool], + description: Optional[Union[str, Pattern[str]]], ) -> "Locator": return self.locator( - get_by_role_selector( + selector=get_by_role_selector( role, checked=checked, disabled=disabled, expanded=expanded, - includeHidden=includeHidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, selected=selected, exact=exact, description=description, - ) + ), + has_text=None, + has_not_text=None, + has=None, + has_not=None, ) - def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator": - return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId)) + def get_by_test_id(self, test_id: Union[str, Pattern[str]]) -> "Locator": + return self.locator( + selector=get_by_test_id_selector(test_id_attribute_name(), test_id), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_text_selector(text, exact=exact)) + return self.locator( + selector=get_by_text_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_title( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_title_selector(text, exact=exact)) + return self.locator( + selector=get_by_title_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) - def frame_locator(self, selector: str = None) -> FrameLocator: + def frame_locator(self, selector: Optional[str]) -> FrameLocator: return FrameLocator(self, ANY_FRAME_SELECTOR if selector is None else selector) async def focus( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> None: - await self._channel.send("focus", self._timeout, locals_to_params(locals())) + await self._channel.focus( + selector=selector, strict=strict, timeout=self._timeout(timeout) + ) async def text_content( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> Optional[str]: - return await self._channel.send( - "textContent", self._timeout, locals_to_params(locals()) + result = await self._channel.textContent( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] async def inner_text( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> str: - return await self._channel.send( - "innerText", self._timeout, locals_to_params(locals()) + result = await self._channel.innerText( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] async def inner_html( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> str: - return await self._channel.send( - "innerHTML", self._timeout, locals_to_params(locals()) + result = await self._channel.innerHTML( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] async def get_attribute( - self, selector: str, name: str, strict: bool = None, timeout: float = None + self, + selector: str, + name: str, + strict: Optional[bool], + timeout: Optional[float], ) -> Optional[str]: - return await self._channel.send( - "getAttribute", self._timeout, locals_to_params(locals()) + result = await self._channel.getAttribute( + selector=selector, + strict=strict, + name=name, + timeout=self._timeout(timeout), ) + return result["value"] async def hover( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. + force: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send("hover", self._timeout, locals_to_params(locals())) + await self._channel.hover( + selector=selector, + strict=strict, + force=force, + scroll=scroll, + modifiers=modifiers, + position=position, + trial=trial, + timeout=self._timeout(timeout), + ) async def drag_and_drop( self, source: str, target: str, - sourcePosition: Position = None, - targetPosition: Position = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - timeout: float = None, - trial: bool = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, + source_position: Optional[Position], + target_position: Optional[Position], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + timeout: Optional[float], + trial: Optional[bool], + steps: Optional[int], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send( - "dragAndDrop", self._timeout, locals_to_params(locals()) + await self._channel.dragAndDrop( + source=source, + target=target, + force=force, + scroll=scroll, + trial=trial, + sourcePosition=source_position, + targetPosition=target_position, + strict=strict, + steps=steps, + timeout=self._timeout(timeout), ) async def select_option( self, selector: str, - value: Union[str, Sequence[str]] = None, - index: Union[int, Sequence[int]] = None, - label: Union[str, Sequence[str]] = None, - element: Union["ElementHandle", Sequence["ElementHandle"]] = None, - timeout: float = None, - noWaitAfter: bool = None, - strict: bool = None, - force: bool = None, + value: Optional[Union[str, Sequence[str]]], + index: Optional[Union[int, Sequence[int]]], + label: Optional[Union[str, Sequence[str]]], + element: Optional[Union["ElementHandle", Sequence["ElementHandle"]]], + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + force: Optional[bool], ) -> List[str]: - params = locals_to_params( - dict( - selector=selector, - timeout=timeout, - strict=strict, - force=force, - **convert_select_option_values(value, index, label, element), - ) + values = convert_select_option_values(value, index, label, element) + result = await self._channel.selectOption( + selector=selector, + strict=strict, + elements=values.get("elements"), + options=values.get("options"), + force=force, + timeout=self._timeout(timeout), ) - return await self._channel.send("selectOption", self._timeout, params) + return result["values"] async def input_value( self, selector: str, - strict: bool = None, - timeout: float = None, + strict: Optional[bool], + timeout: Optional[float], ) -> str: - return await self._channel.send( - "inputValue", self._timeout, locals_to_params(locals()) + result = await self._channel.inputValue( + selector=selector, strict=strict, timeout=self._timeout(timeout) ) + return result["value"] async def set_input_files( self, @@ -838,158 +963,200 @@ async def set_input_files( files: Union[ str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload] ], - strict: bool = None, - timeout: float = None, - noWaitAfter: bool = None, + strict: Optional[bool], + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. ) -> None: converted = await convert_input_files(files, self.page.context) - await self._channel.send( - "setInputFiles", - self._timeout, - { - "selector": selector, - "strict": strict, - "timeout": self._timeout(timeout), - **converted, - }, + await self._channel.setInputFiles( + selector=selector, + strict=strict, + payloads=converted.get("payloads"), + localDirectory=converted.get("localDirectory"), + directoryStream=converted.get("directoryStream"), + localPaths=converted.get("localPaths"), + streams=converted.get("streams"), + timeout=self._timeout(timeout), ) async def _drop( self, selector: str, payload: "DropPayload", - strict: bool = None, - position: Position = None, - timeout: float = None, + strict: Optional[bool], + position: Optional[Position], + timeout: Optional[float], ) -> None: - params: Dict[str, Any] = { - "selector": selector, - "strict": strict, - "position": position, - "timeout": self._timeout(timeout), - } + payloads = None + local_paths = None + streams = None files = payload.get("files") if payload else None if files is not None: converted = await convert_input_files(files, self.page.context) - if "directoryStream" in converted or "directoryLocalPath" in converted: + if converted.get("directoryStream") or converted.get("localDirectory"): raise Error( "Dropping a directory is not supported, pass individual files instead." ) - params.update(converted) + payloads = converted.get("payloads") + local_paths = converted.get("localPaths") + streams = converted.get("streams") data = payload.get("data") if payload else None - if data is not None: - params["data"] = [{"mimeType": k, "value": v} for k, v in data.items()] - await self._channel.send("drop", self._timeout, params) + await self._channel.drop( + selector=selector, + strict=strict, + position=position, + payloads=payloads, + localPaths=local_paths, + streams=streams, + data=( + [{"mimeType": k, "value": v} for k, v in data.items()] + if data is not None + else None + ), + timeout=self._timeout(timeout), + ) async def type( self, selector: str, text: str, - delay: float = None, - strict: bool = None, - timeout: float = None, - noWaitAfter: bool = None, + delay: Optional[float], + strict: Optional[bool], + timeout: Optional[float], + no_wait_after: Optional[bool], # Deprecated; does nothing. ) -> None: - await self._channel.send("type", self._timeout, locals_to_params(locals())) + await self._channel.type( + selector=selector, + strict=strict, + text=text, + delay=delay, + timeout=self._timeout(timeout), + ) async def press( self, selector: str, key: str, - delay: float = None, - strict: bool = None, - timeout: float = None, - noWaitAfter: bool = None, + delay: Optional[float], + strict: Optional[bool], + timeout: Optional[float], + no_wait_after: Optional[bool], ) -> None: - await self._channel.send("press", self._timeout, locals_to_params(locals())) + await self._channel.press( + selector=selector, + strict=strict, + key=key, + delay=delay, + noWaitAfter=no_wait_after, + timeout=self._timeout(timeout), + ) async def check( self, selector: str, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send("check", self._timeout, locals_to_params(locals())) + await self._channel.check( + selector=selector, + strict=strict, + force=force, + scroll=scroll, + position=position, + trial=trial, + timeout=self._timeout(timeout), + ) async def uncheck( self, selector: str, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], # Deprecated; does nothing. + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - await self._channel.send("uncheck", self._timeout, locals_to_params(locals())) + await self._channel.uncheck( + selector=selector, + strict=strict, + force=force, + scroll=scroll, + position=position, + trial=trial, + timeout=self._timeout(timeout), + ) async def wait_for_timeout(self, timeout: float) -> None: - await self._channel.send("waitForTimeout", None, {"waitTimeout": timeout}) + await self._channel.waitForTimeout(waitTimeout=timeout) async def wait_for_function( self, expression: str, - arg: Serializable = None, - timeout: float = None, - polling: Union[float, Literal["raf"]] = None, + arg: Serializable, + timeout: Optional[float], + polling: Optional[Union[float, Literal["raf"]]], ) -> JSHandle: if isinstance(polling, str) and polling != "raf": raise Error(f"Unknown polling option: {polling}") - params = locals_to_params(locals()) - params.pop("polling", None) - params["arg"] = serialize_argument(arg) - if polling is not None and polling != "raf": - params["pollingInterval"] = polling - return from_channel( - await self._channel.send("waitForFunction", self._timeout, params) + result = await self._channel.waitForFunction( + expression=expression, + isFunction=None, + arg=serialize_argument(arg), + # "raf" is the default polling, sent as no interval. + pollingInterval=None if isinstance(polling, str) else polling, + selector=None, + strict=None, + timeout=self._timeout(timeout), ) + return from_channel(cast("JSHandleChannel", result["handle"])) async def title(self) -> str: - return await self._channel.send("title", None) + return (await self._channel.title())["value"] async def set_checked( self, selector: str, checked: bool, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: if checked: await self.check( - selector=selector, + selector, position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, strict=strict, trial=trial, scroll=scroll, ) else: await self.uncheck( - selector=selector, + selector, position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, strict=strict, trial=trial, scroll=scroll, ) - async def _highlight(self, selector: str, style: str = None) -> None: - await self._channel.send( - "highlight", None, {"selector": selector, "style": style} - ) + async def _highlight(self, selector: str, style: Optional[str]) -> None: + await self._channel.highlight(selector=selector, style=style) async def _hide_highlight(self, selector: str) -> None: - await self._channel.send("hideHighlight", None, {"selector": selector}) + await self._channel.hideHighlight(selector=selector) diff --git a/playwright/_impl/_har_router.py b/playwright/_impl/_har_router.py index 9b92f706c..718b91cdd 100644 --- a/playwright/_impl/_har_router.py +++ b/playwright/_impl/_har_router.py @@ -13,14 +13,14 @@ # limitations under the License. import asyncio import base64 -from typing import TYPE_CHECKING, Optional, cast +from typing import TYPE_CHECKING, Optional +from playwright._impl._errors import Error from playwright._impl._helper import ( - HarLookupResult, RouteFromHarNotFoundPolicy, URLMatch, + create_task_and_ignore_exception, ) -from playwright._impl._internal_structures import HeadersArray from playwright._impl._local_utils import LocalUtils if TYPE_CHECKING: # pragma: no cover @@ -49,7 +49,12 @@ async def create( not_found_action: RouteFromHarNotFoundPolicy, url_matcher: Optional[URLMatch] = None, ) -> "HarRouter": - har_id = await local_utils._channel.send("harOpen", None, {"file": file}) + result = await local_utils._channel.harOpen(file=file) + if result["error"]: + raise Error(result["error"]) + har_id = result["harId"] + if har_id is None: + raise Error("harOpen returned no HAR id") return HarRouter( local_utils=local_utils, har_id=har_id, @@ -59,13 +64,13 @@ async def create( async def _handle(self, route: "Route") -> None: request = route.request - response: HarLookupResult = await self._local_utils.har_lookup( - harId=self._har_id, + response = await self._local_utils.har_lookup( + har_id=self._har_id, url=request.url, method=request.method, headers=await request.headers_array(), - postData=request.post_data_buffer, - isNavigationRequest=request.is_navigation_request(), + post_data=request.post_data_buffer, + is_navigation_request=request.is_navigation_request(), ) action = response["action"] if action == "redirect": @@ -80,17 +85,18 @@ async def _handle(self, route: "Route") -> None: # TODO: it'd be better to abort such requests, but then we likely need to respect the timing, # because the request might have been stalled for a long time until the very end of the # test when HAR was recorded but we'd abort it immediately. - if response.get("status") == -1: + if response["status"] == -1: return body = response["body"] assert body is not None await route.fulfill( - status=response.get("status"), - headers={ - v["name"]: v["value"] - for v in cast(HeadersArray, response.get("headers", [])) - }, + status=response["status"], + headers={v["name"]: v["value"] for v in response["headers"] or []}, body=base64.b64decode(body), + json=None, + path=None, + content_type=None, + response=None, ) return @@ -99,24 +105,27 @@ async def _handle(self, route: "Route") -> None: # Report the error, but fall through to the default handler. if self._not_found_action == "abort": - await route.abort() + await route.abort(error_code=None) return - await route.fallback() + await route.fallback(url=None, method=None, headers=None, post_data=None) async def add_context_route(self, context: "BrowserContext") -> None: await context.route( url=self._options_url_match or "**/*", handler=lambda route, _: asyncio.create_task(self._handle(route)), + times=None, ) async def add_page_route(self, page: "Page") -> None: await page.route( url=self._options_url_match or "**/*", handler=lambda route, _: asyncio.create_task(self._handle(route)), + times=None, ) def dispose(self) -> None: - self._local_utils._channel.send_may_fail( - "harClose", None, {"harId": self._har_id} + create_task_and_ignore_exception( + self._local_utils._loop, + self._local_utils._channel.harClose(harId=self._har_id), ) diff --git a/playwright/_impl/_helper.py b/playwright/_impl/_helper.py index 5a0e182fb..fdebadd27 100644 --- a/playwright/_impl/_helper.py +++ b/playwright/_impl/_helper.py @@ -35,13 +35,13 @@ overload, ) -from playwright._impl._errors import ErrorPayload, is_target_closed_error, rewrite_error +from playwright._impl._errors import is_target_closed_error, rewrite_error from playwright._impl._greenlets import RouteGreenlet from playwright._impl._str_utils import escape_regex_flags from playwright._impl._url_match import URLMatch, url_matches if TYPE_CHECKING: # pragma: no cover - from playwright._impl._internal_structures import HeadersArray + from playwright._impl import _protocol_types as channels from playwright._impl._network import Request, Response, Route, WebSocketRoute URLMatchRequest = Union[ @@ -73,35 +73,6 @@ class HarRecordingMetadata(TypedDict, total=False): content: Optional[HarContentPolicy] -def prepare_record_har_options(params: Dict) -> Dict[str, Any]: - out_params: Dict[str, Any] = {"path": str(params["recordHarPath"])} - if "recordHarUrlFilter" in params: - opt = params["recordHarUrlFilter"] - if isinstance(opt, str): - out_params["urlGlob"] = opt - if isinstance(opt, Pattern): - out_params["urlRegexSource"] = opt.pattern - out_params["urlRegexFlags"] = escape_regex_flags(opt) - del params["recordHarUrlFilter"] - if "recordHarMode" in params: - out_params["mode"] = params["recordHarMode"] - del params["recordHarMode"] - - new_content_api = None - old_content_api = None - if "recordHarContent" in params: - new_content_api = params["recordHarContent"] - del params["recordHarContent"] - if "recordHarOmitContent" in params: - old_content_api = params["recordHarOmitContent"] - del params["recordHarOmitContent"] - content = new_content_api or ("omit" if old_content_api else None) - if content: - out_params["content"] = content - - return out_params - - class ParsedMessageParams(TypedDict): type: str guid: str @@ -114,51 +85,17 @@ class ParsedMessagePayload(TypedDict, total=False): method: str params: ParsedMessageParams result: Any - error: ErrorPayload + error: "channels.SerializedError" errorDetails: Any log: List[str] -class Document(TypedDict): - request: Optional[Any] - - -class FrameNavigatedEvent(TypedDict): - url: str - name: str - newDocument: Optional[Document] - error: Optional[str] - - Env = Dict[str, Union[str, float, bool]] -class HarLookupResult(TypedDict, total=False): - action: Literal["error", "redirect", "fulfill", "noentry"] - message: Optional[str] - redirectURL: Optional[str] - status: Optional[int] - headers: Optional["HeadersArray"] - body: Optional[str] - - PLAYWRIGHT_MAX_DEADLINE = 2147483647 # 2^31-1 -def locals_to_params(args: Dict) -> Dict: - copy = {} - for key in args: - if key == "self": - continue - if args[key] is not None: - copy[key] = ( - args[key] - if not isinstance(args[key], Dict) - else locals_to_params(args[key]) - ) - return copy - - def create_task_and_ignore_exception( loop: asyncio.AbstractEventLoop, coro: Awaitable[Any] ) -> None: @@ -272,8 +209,10 @@ def will_expire(self) -> bool: @staticmethod def prepare_interception_patterns( handlers: List["RouteHandler"], - ) -> List[Dict[str, str]]: - patterns = [] + ) -> List["channels.BrowserContextSetNetworkInterceptionPatternsParamsPatterns"]: + patterns: List[ + "channels.BrowserContextSetNetworkInterceptionPatternsParamsPatterns" + ] = [] all = False for handler in handlers: if isinstance(handler.url, str): diff --git a/playwright/_impl/_input.py b/playwright/_impl/_input.py index 1568885f2..8e185cf88 100644 --- a/playwright/_impl/_input.py +++ b/playwright/_impl/_input.py @@ -12,100 +12,112 @@ # See the License for the specific language governing permissions and # limitations under the License. -from playwright._impl._connection import Channel -from playwright._impl._helper import MouseButton, locals_to_params +from typing import TYPE_CHECKING, Optional + +from playwright._impl._helper import MouseButton + +if TYPE_CHECKING: # pragma: no cover + from playwright._impl._protocol_channels import PageChannel class Keyboard: - def __init__(self, channel: Channel) -> None: + def __init__(self, channel: "PageChannel") -> None: self._channel = channel self._loop = channel._connection._loop self._dispatcher_fiber = channel._connection._dispatcher_fiber async def down(self, key: str) -> None: - await self._channel.send("keyboardDown", None, locals_to_params(locals())) + await self._channel.keyboardDown(key=key) async def up(self, key: str) -> None: - await self._channel.send("keyboardUp", None, locals_to_params(locals())) + await self._channel.keyboardUp(key=key) async def insert_text(self, text: str) -> None: - await self._channel.send("keyboardInsertText", None, locals_to_params(locals())) + await self._channel.keyboardInsertText(text=text) - async def type(self, text: str, delay: float = None) -> None: - await self._channel.send("keyboardType", None, locals_to_params(locals())) + async def type(self, text: str, delay: Optional[float]) -> None: + await self._channel.keyboardType(text=text, delay=delay) - async def press(self, key: str, delay: float = None) -> None: - await self._channel.send("keyboardPress", None, locals_to_params(locals())) + async def press(self, key: str, delay: Optional[float]) -> None: + await self._channel.keyboardPress(key=key, delay=delay) class Mouse: - def __init__(self, channel: Channel) -> None: + def __init__(self, channel: "PageChannel") -> None: self._channel = channel self._loop = channel._connection._loop self._dispatcher_fiber = channel._connection._dispatcher_fiber - async def move(self, x: float, y: float, steps: int = None) -> None: - await self._channel.send("mouseMove", None, locals_to_params(locals())) + async def move(self, x: float, y: float, steps: Optional[int]) -> None: + await self._channel.mouseMove(x=x, y=y, steps=steps) async def down( self, - button: MouseButton = None, - clickCount: int = None, + button: Optional[MouseButton], + click_count: Optional[int], ) -> None: - await self._channel.send("mouseDown", None, locals_to_params(locals())) + await self._channel.mouseDown(button=button, clickCount=click_count) async def up( self, - button: MouseButton = None, - clickCount: int = None, + button: Optional[MouseButton], + click_count: Optional[int], ) -> None: - await self._channel.send("mouseUp", None, locals_to_params(locals())) + await self._channel.mouseUp(button=button, clickCount=click_count) async def _click( self, x: float, y: float, - delay: float = None, - button: MouseButton = None, - clickCount: int = None, - title: str = None, + delay: Optional[float], + button: Optional[MouseButton], + click_count: Optional[int], + title: Optional[str], ) -> None: - params = locals_to_params(locals()) - params.pop("title", None) - await self._channel.send("mouseClick", None, params, title=title) + await self._channel._connection.wrap_api_call( + lambda: self._channel.mouseClick( + x=x, y=y, delay=delay, button=button, clickCount=click_count + ), + title=title, + ) async def click( self, x: float, y: float, - delay: float = None, - button: MouseButton = None, - clickCount: int = None, + delay: Optional[float], + button: Optional[MouseButton], + click_count: Optional[int], ) -> None: - params = locals() - del params["self"] - await self._click(**params) + await self._click( + x, + y, + delay=delay, + button=button, + click_count=click_count, + title=None, + ) async def dblclick( self, x: float, y: float, - delay: float = None, - button: MouseButton = None, + delay: Optional[float], + button: Optional[MouseButton], ) -> None: await self._click( - x, y, delay=delay, button=button, clickCount=2, title="Double click" + x, y, delay=delay, button=button, click_count=2, title="Double click" ) - async def wheel(self, deltaX: float, deltaY: float) -> None: - await self._channel.send("mouseWheel", None, locals_to_params(locals())) + async def wheel(self, delta_x: float, delta_y: float) -> None: + await self._channel.mouseWheel(deltaX=delta_x, deltaY=delta_y) class Touchscreen: - def __init__(self, channel: Channel) -> None: + def __init__(self, channel: "PageChannel") -> None: self._channel = channel self._loop = channel._connection._loop self._dispatcher_fiber = channel._connection._dispatcher_fiber async def tap(self, x: float, y: float) -> None: - await self._channel.send("touchscreenTap", None, locals_to_params(locals())) + await self._channel.touchscreenTap(x=x, y=y) diff --git a/playwright/_impl/_internal_structures.py b/playwright/_impl/_internal_structures.py index d188739fe..b3baef46a 100644 --- a/playwright/_impl/_internal_structures.py +++ b/playwright/_impl/_internal_structures.py @@ -27,25 +27,13 @@ class NameValue(TypedDict): Headers = Dict[str, str] -class ServerFilePayload(TypedDict): - name: str - mimeType: str - buffer: str - - -class FormField(TypedDict, total=False): - name: str - value: Optional[str] - file: Optional[ServerFilePayload] - - class ExpectedTextValue(TypedDict, total=False): string: str regexSource: str regexFlags: str matchSubstring: bool normalizeWhiteSpace: bool - ignoreCase: Optional[bool] + ignoreCase: bool class FrameExpectOptions(TypedDict, total=False): @@ -56,7 +44,7 @@ class FrameExpectOptions(TypedDict, total=False): useInnerText: Optional[bool] isNot: bool timeout: Optional[float] - pseudo: Optional[str] + pseudo: Optional[Literal["before", "after"]] class FrameExpectResult(TypedDict, total=False): diff --git a/playwright/_impl/_js_handle.py b/playwright/_impl/_js_handle.py index 296aa7ca5..b629fbebe 100644 --- a/playwright/_impl/_js_handle.py +++ b/playwright/_impl/_js_handle.py @@ -22,21 +22,23 @@ from playwright._impl._serializers import parse_value, serialize_value if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._element_handle import ElementHandle + from playwright._impl._protocol_channels import JSHandleChannel Serializable = Any class JSHandle(ChannelOwner): + _channel: "JSHandleChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: super().__init__(parent, type, guid, initializer) self._preview = self._initializer["preview"] - self._channel.on( - "previewUpdated", lambda params: self._on_preview_updated(params["preview"]) - ) + self._channel.onPreviewUpdated(self._on_preview_updated) def __repr__(self) -> str: return f"" @@ -44,47 +46,32 @@ def __repr__(self) -> str: def __str__(self) -> str: return self._preview - def _on_preview_updated(self, preview: str) -> None: - self._preview = preview - - async def evaluate(self, expression: str, arg: Serializable = None) -> Any: - return parse_result( - await self._channel.send( - "evaluateExpression", - None, - dict( - expression=expression, - arg=serialize_argument(arg), - ), - ) + def _on_preview_updated( + self, event: "channels.JSHandlePreviewUpdatedEvent" + ) -> None: + self._preview = event["preview"] + + async def evaluate(self, expression: str, arg: Serializable) -> Any: + result = await self._channel.evaluateExpression( + expression=expression, isFunction=None, arg=serialize_argument(arg) ) + return parse_result(result["value"]) - async def evaluate_handle( - self, expression: str, arg: Serializable = None - ) -> "JSHandle": - return from_channel( - await self._channel.send( - "evaluateExpressionHandle", - None, - dict( - expression=expression, - arg=serialize_argument(arg), - ), - ) + async def evaluate_handle(self, expression: str, arg: Serializable) -> "JSHandle": + result = await self._channel.evaluateExpressionHandle( + expression=expression, isFunction=None, arg=serialize_argument(arg) ) + return from_channel(result["handle"]) - async def get_property(self, propertyName: str) -> "JSHandle": + async def get_property(self, property_name: str) -> "JSHandle": return from_channel( - await self._channel.send("getProperty", None, dict(name=propertyName)) + (await self._channel.getProperty(name=property_name))["handle"] ) async def get_properties(self) -> Dict[str, "JSHandle"]: + result = await self._channel.getPropertyList() return { - prop["name"]: from_channel(prop["value"]) - for prop in await self._channel.send( - "getPropertyList", - None, - ) + prop["name"]: from_channel(prop["value"]) for prop in result["properties"] } def as_element(self) -> Optional["ElementHandle"]: @@ -92,21 +79,13 @@ def as_element(self) -> Optional["ElementHandle"]: async def dispose(self) -> None: try: - await self._channel.send( - "dispose", - None, - ) + await self._channel.dispose() except Exception as e: if not is_target_closed_error(e): raise e async def json_value(self) -> Any: - return parse_result( - await self._channel.send( - "jsonValue", - None, - ) - ) + return parse_result((await self._channel.jsonValue())["value"]) def serialize_argument(arg: Serializable = None) -> Any: diff --git a/playwright/_impl/_json_pipe.py b/playwright/_impl/_json_pipe.py index 2eec6a32c..d38f41dd7 100644 --- a/playwright/_impl/_json_pipe.py +++ b/playwright/_impl/_json_pipe.py @@ -13,21 +13,23 @@ # limitations under the License. import asyncio -from typing import Dict, Optional, cast +from typing import TYPE_CHECKING, Dict, Optional, cast from pyee.asyncio import AsyncIOEventEmitter -from playwright._impl._connection import Channel from playwright._impl._errors import Error, TargetClosedError from playwright._impl._helper import ParsedMessagePayload from playwright._impl._transport import Transport +if TYPE_CHECKING: # pragma: no cover + from playwright._impl._protocol_channels import JsonPipeChannel + class JsonPipeTransport(AsyncIOEventEmitter, Transport): def __init__( self, loop: asyncio.AbstractEventLoop, - pipe_channel: Channel, + pipe_channel: "JsonPipeChannel", ) -> None: super().__init__(loop) Transport.__init__(self, loop) @@ -36,7 +38,7 @@ def __init__( def request_stop(self) -> None: self._stop_requested = True - self._pipe_channel.send_no_reply("close", None, {}) + self._pipe_channel.send_no_reply("close") def dispose(self) -> None: self.on_error_future.cancel() @@ -59,14 +61,8 @@ def handle_closed(reason: Optional[str]) -> None: self.on_error_future.set_exception(TargetClosedError(reason)) self._stopped_future.set_result(None) - self._pipe_channel.on( - "message", - lambda params: handle_message(params["message"]), - ) - self._pipe_channel.on( - "closed", - lambda params: handle_closed(params.get("reason")), - ) + self._pipe_channel.onMessage(lambda event: handle_message(event["message"])) + self._pipe_channel.onClosed(lambda event: handle_closed(event["reason"])) async def run(self) -> None: await self._stopped_future @@ -74,4 +70,5 @@ async def run(self) -> None: def send(self, message: Dict) -> None: if self._stop_requested: raise Error("Playwright connection closed") - self._pipe_channel.send_no_reply("send", None, {"message": message}) + # The transport sends synchronously, so this does not wait for the reply. + self._pipe_channel.send_no_reply("send", {"message": message}) diff --git a/playwright/_impl/_local_utils.py b/playwright/_impl/_local_utils.py index 7813c5279..7bbbc8576 100644 --- a/playwright/_impl/_local_utils.py +++ b/playwright/_impl/_local_utils.py @@ -13,14 +13,19 @@ # limitations under the License. import base64 -from typing import Dict, List, Optional, cast +from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Sequence, cast from playwright._impl._connection import ChannelOwner, StackFrame -from playwright._impl._helper import HarLookupResult, locals_to_params from playwright._impl._internal_structures import HeadersArray +if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels + from playwright._impl._protocol_channels import LocalUtilsChannel + class LocalUtils(ChannelOwner): + _channel: "LocalUtilsChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -30,58 +35,73 @@ def __init__( for device in initializer["deviceDescriptors"] } - async def zip(self, params: Dict) -> None: - await self._channel.send("zip", None, params) + async def zip( + self, + zip_file: str, + entries: Sequence["channels.NameValue"], + stacks_id: Optional[str], + mode: Literal["write", "append"], + include_sources: bool, + additional_sources: Optional[Sequence[str]], + ) -> None: + await self._channel.zip( + zipFile=zip_file, + entries=entries, + stacksId=stacks_id, + mode=mode, + includeSources=include_sources, + additionalSources=additional_sources, + ) async def har_open(self, file: str) -> None: - params = locals_to_params(locals()) - await self._channel.send("harOpen", None, params) + await self._channel.harOpen(file=file) async def har_lookup( self, - harId: str, + har_id: str, url: str, method: str, headers: HeadersArray, - isNavigationRequest: bool, - postData: Optional[bytes] = None, - ) -> HarLookupResult: - params = locals_to_params(locals()) - if "postData" in params: - params["postData"] = base64.b64encode(params["postData"]).decode() - return cast( - HarLookupResult, - await self._channel.send_return_as_dict("harLookup", None, params), + is_navigation_request: bool, + post_data: Optional[bytes], + ) -> "channels.LocalUtilsHarLookupResult": + return await self._channel.harLookup( + harId=har_id, + url=url, + method=method, + headers=headers, + postData=( + base64.b64encode(post_data).decode() if post_data is not None else None + ), + isNavigationRequest=is_navigation_request, ) - async def har_close(self, harId: str) -> None: - params = locals_to_params(locals()) - await self._channel.send("harClose", None, params) + async def har_close(self, har_id: str) -> None: + await self._channel.harClose(harId=har_id) - async def har_unzip(self, zipFile: str, harFile: str) -> None: - params = locals_to_params(locals()) - await self._channel.send("harUnzip", None, params) + async def har_unzip(self, zip_file: str, har_file: str) -> None: + await self._channel.harUnzip( + zipFile=zip_file, harFile=har_file, resourcesDir=None + ) async def tracing_started( - self, tracesDir: Optional[str], traceName: str, live: bool = False + self, traces_dir: Optional[str], trace_name: str, live: bool ) -> str: - params = locals_to_params(locals()) - return await self._channel.send("tracingStarted", None, params) + result = await self._channel.tracingStarted( + tracesDir=traces_dir, traceName=trace_name, live=live + ) + return result["stacksId"] async def trace_discarded(self, stacks_id: str) -> None: - return await self._channel.send("traceDiscarded", None, {"stacksId": stacks_id}) + await self._channel.traceDiscarded(stacksId=stacks_id) def add_stack_to_tracing_no_reply(self, id: str, frames: List[StackFrame]) -> None: - self._channel.send_no_reply( - "addStackToTracingNoReply", - None, - { - "callData": { - "stack": frames, - "id": id, - } - }, - ) + call_data: channels.ClientSideCallMetadata = { + # The client always sets "function" in its frames. + "stack": cast("List[channels.StackFrame]", frames), + "id": id, + } + self._channel.send_no_reply("addStackToTracingNoReply", {"callData": call_data}) def parse_device_descriptor(dict: Dict) -> Dict: diff --git a/playwright/_impl/_locator.py b/playwright/_impl/_locator.py index 6d98b2bbd..83f16f4c7 100644 --- a/playwright/_impl/_locator.py +++ b/playwright/_impl/_locator.py @@ -42,7 +42,6 @@ from playwright._impl._helper import ( KeyboardModifier, MouseButton, - locals_to_params, to_impl, ) from playwright._impl._internal_structures import ( @@ -145,7 +144,7 @@ def _equals(self, locator: "Locator") -> bool: def page(self) -> "Page": return self._frame.page - async def bounding_box(self, timeout: float = None) -> Optional[FloatRect]: + async def bounding_box(self, timeout: Optional[float]) -> Optional[FloatRect]: return await self._with_element( lambda h, _: h.bounding_box(), timeout, @@ -153,193 +152,286 @@ async def bounding_box(self, timeout: float = None) -> Optional[FloatRect]: async def check( self, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - params = locals_to_params(locals()) - return await self._frame.check(self._selector, strict=True, **params) + await self._frame.check( + self._selector, + strict=True, + position=position, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + trial=trial, + scroll=scroll, + ) async def click( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - clickCount: int = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + click_count: Optional[int], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + steps: Optional[int], + scroll: Optional[Literal["auto", "none"]], ) -> None: - params = locals_to_params(locals()) - return await self._frame._click(self._selector, strict=True, **params) + await self._frame.click( + self._selector, + strict=True, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + click_count=click_count, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + trial=trial, + steps=steps, + scroll=scroll, + ) async def dblclick( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + steps: Optional[int], + scroll: Optional[Literal["auto", "none"]], ) -> None: - params = locals_to_params(locals()) - return await self._frame.dblclick(self._selector, strict=True, **params) + await self._frame.dblclick( + self._selector, + strict=True, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + trial=trial, + steps=steps, + scroll=scroll, + ) async def dispatch_event( self, type: str, - eventInit: Dict = None, - timeout: float = None, + event_init: Optional[Dict], + timeout: Optional[float], ) -> None: - params = locals_to_params(locals()) - return await self._frame.dispatch_event(self._selector, strict=True, **params) + await self._frame.dispatch_event( + self._selector, + type=type, + event_init=event_init, + strict=True, + timeout=timeout, + ) async def evaluate( - self, expression: str, arg: Serializable = None, timeout: float = None + self, expression: str, arg: Serializable, timeout: Optional[float] ) -> Any: return await self._with_element( - lambda h, _: h.evaluate(expression, arg), + lambda h, _: h.evaluate(expression=expression, arg=arg), timeout, ) - async def evaluate_all(self, expression: str, arg: Serializable = None) -> Any: - params = locals_to_params(locals()) - return await self._frame.eval_on_selector_all(self._selector, **params) + async def evaluate_all(self, expression: str, arg: Serializable) -> Any: + return await self._frame.eval_on_selector_all( + self._selector, expression=expression, arg=arg + ) async def evaluate_handle( - self, expression: str, arg: Serializable = None, timeout: float = None + self, expression: str, arg: Serializable, timeout: Optional[float] ) -> "JSHandle": return await self._with_element( - lambda h, _: h.evaluate_handle(expression, arg), timeout + lambda h, _: h.evaluate_handle(expression=expression, arg=arg), timeout ) async def fill( self, value: str, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], + force: Optional[bool], ) -> None: - params = locals_to_params(locals()) - return await self._frame.fill(self._selector, strict=True, **params) + await self._frame.fill( + self._selector, + value, + strict=True, + timeout=timeout, + no_wait_after=no_wait_after, + force=force, + ) async def clear( self, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], + force: Optional[bool], ) -> None: - params = locals_to_params(locals()) - await self._frame._fill(self._selector, value="", title="Clear", **params) + await self._frame._fill( + self._selector, + "", + title="Clear", + strict=True, + timeout=timeout, + no_wait_after=no_wait_after, + force=force, + ) def locator( self, - selectorOrLocator: Union[str, "Locator"], - hasText: Union[str, Pattern[str]] = None, - hasNotText: Union[str, Pattern[str]] = None, - has: "Locator" = None, - hasNot: "Locator" = None, + selector_or_locator: Union[str, "Locator"], + has_text: Optional[Union[str, Pattern[str]]], + has_not_text: Optional[Union[str, Pattern[str]]], + has: Optional["Locator"], + has_not: Optional["Locator"], ) -> "Locator": - if isinstance(selectorOrLocator, str): + if isinstance(selector_or_locator, str): return Locator( self._frame, - f"{self._selector} >> {selectorOrLocator}", - has_text=hasText, - has_not_text=hasNotText, - has_not=hasNot, + f"{self._selector} >> {selector_or_locator}", + has_text=has_text, + has_not_text=has_not_text, + has_not=has_not, has=has, ) - selectorOrLocator = to_impl(selectorOrLocator) - if selectorOrLocator._frame != self._frame: + selector_or_locator = to_impl(selector_or_locator) + if selector_or_locator._frame != self._frame: raise Error("Locators must belong to the same frame.") return Locator( self._frame, - f"{self._selector} >> internal:chain={json.dumps(selectorOrLocator._selector)}", - has_text=hasText, - has_not_text=hasNotText, - has_not=hasNot, + f"{self._selector} >> internal:chain={json.dumps(selector_or_locator._selector)}", + has_text=has_text, + has_not_text=has_not_text, + has_not=has_not, has=has, ) def get_by_alt_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_alt_text_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_alt_text_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_label( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_label_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_label_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_placeholder( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_placeholder_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_placeholder_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_role( self, role: AriaRole, - checked: bool = None, - disabled: bool = None, - expanded: bool = None, - includeHidden: bool = None, - level: int = None, - name: Union[str, Pattern[str]] = None, - pressed: bool = None, - selected: bool = None, - exact: bool = None, - description: Union[str, Pattern[str]] = None, + checked: Optional[bool], + disabled: Optional[bool], + expanded: Optional[bool], + include_hidden: Optional[bool], + level: Optional[int], + name: Optional[Union[str, Pattern[str]]], + pressed: Optional[bool], + selected: Optional[bool], + exact: Optional[bool], + description: Optional[Union[str, Pattern[str]]], ) -> "Locator": return self.locator( - get_by_role_selector( + selector_or_locator=get_by_role_selector( role, checked=checked, disabled=disabled, expanded=expanded, - includeHidden=includeHidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, selected=selected, exact=exact, description=description, - ) + ), + has_text=None, + has_not_text=None, + has=None, + has_not=None, ) - def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator": - return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId)) + def get_by_test_id(self, test_id: Union[str, Pattern[str]]) -> "Locator": + return self.locator( + selector_or_locator=get_by_test_id_selector( + test_id_attribute_name(), test_id + ), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_text_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_text_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_title( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_title_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_title_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def frame_locator(self, selector: str) -> "FrameLocator": return FrameLocator(self._frame, self._selector + " >> " + selector) async def element_handle( self, - timeout: float = None, + timeout: Optional[float], ) -> ElementHandle: - params = locals_to_params(locals()) handle = await self._frame.wait_for_selector( - self._selector, strict=True, state="attached", **params + self._selector, strict=True, state="attached", timeout=timeout ) assert handle return handle @@ -384,19 +476,19 @@ def description(self) -> Optional[str]: def filter( self, - hasText: Union[str, Pattern[str]] = None, - hasNotText: Union[str, Pattern[str]] = None, - has: "Locator" = None, - hasNot: "Locator" = None, - visible: bool = None, + has_text: Optional[Union[str, Pattern[str]]], + has_not_text: Optional[Union[str, Pattern[str]]], + has: Optional["Locator"], + has_not: Optional["Locator"], + visible: Optional[bool], ) -> "Locator": return Locator( self._frame, self._selector, - has_text=hasText, - has_not_text=hasNotText, + has_text=has_text, + has_not_text=has_not_text, has=has, - has_not=hasNot, + has_not=has_not, visible=visible, ) @@ -421,21 +513,23 @@ def and_(self, locator: "Locator") -> "Locator": ) def within(self, locator: "Locator") -> "Locator": - return locator.locator(self) + return locator.locator( + selector_or_locator=self, + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) - async def focus(self, timeout: float = None) -> None: - params = locals_to_params(locals()) - return await self._frame.focus(self._selector, strict=True, **params) + async def focus( + self, + timeout: Optional[float], + ) -> None: + await self._frame.focus(self._selector, strict=True, timeout=timeout) - async def blur(self, timeout: float = None) -> None: - await self._frame._channel.send( - "blur", - self._frame._timeout, - { - "selector": self._selector, - "strict": True, - **locals_to_params(locals()), - }, + async def blur(self, timeout: Optional[float]) -> None: + await self._frame._channel.blur( + selector=self._selector, strict=True, timeout=self._frame._timeout(timeout) ) async def all( @@ -454,26 +548,34 @@ async def count( async def drag_to( self, target: "Locator", - force: bool = None, - noWaitAfter: bool = None, - timeout: float = None, - trial: bool = None, - sourcePosition: Position = None, - targetPosition: Position = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, + force: Optional[bool], + no_wait_after: Optional[bool], + timeout: Optional[float], + trial: Optional[bool], + source_position: Optional[Position], + target_position: Optional[Position], + steps: Optional[int], + scroll: Optional[Literal["auto", "none"]], ) -> None: - params = locals_to_params(locals()) - del params["target"] return await self._frame.drag_and_drop( - self._selector, target._selector, strict=True, **params + self._selector, + target._selector, + strict=True, + force=force, + no_wait_after=no_wait_after, + timeout=timeout, + trial=trial, + source_position=source_position, + target_position=target_position, + steps=steps, + scroll=scroll, ) async def drop( self, payload: DropPayload, - position: Position = None, - timeout: float = None, + position: Optional[Position], + timeout: Optional[float], ) -> None: await self._frame._drop( self._selector, @@ -483,96 +585,106 @@ async def drop( timeout=timeout, ) - async def get_attribute(self, name: str, timeout: float = None) -> Optional[str]: - params = locals_to_params(locals()) + async def get_attribute( + self, + name: str, + timeout: Optional[float], + ) -> Optional[str]: return await self._frame.get_attribute( - self._selector, - strict=True, - **params, + self._selector, name, strict=True, timeout=timeout ) async def hover( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + no_wait_after: Optional[bool], + force: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - params = locals_to_params(locals()) - return await self._frame.hover( + await self._frame.hover( self._selector, strict=True, - **params, + modifiers=modifiers, + position=position, + timeout=timeout, + no_wait_after=no_wait_after, + force=force, + trial=trial, + scroll=scroll, ) - async def inner_html(self, timeout: float = None) -> str: - params = locals_to_params(locals()) + async def inner_html( + self, + timeout: Optional[float], + ) -> str: return await self._frame.inner_html( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) - async def inner_text(self, timeout: float = None) -> str: - params = locals_to_params(locals()) + async def inner_text( + self, + timeout: Optional[float], + ) -> str: return await self._frame.inner_text( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) - async def input_value(self, timeout: float = None) -> str: - params = locals_to_params(locals()) + async def input_value( + self, + timeout: Optional[float], + ) -> str: return await self._frame.input_value( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) - async def is_checked(self, timeout: float = None) -> bool: - params = locals_to_params(locals()) + async def is_checked( + self, + timeout: Optional[float], + ) -> bool: return await self._frame.is_checked( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) - async def is_disabled(self, timeout: float = None) -> bool: - params = locals_to_params(locals()) + async def is_disabled( + self, + timeout: Optional[float], + ) -> bool: return await self._frame.is_disabled( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) - async def is_editable(self, timeout: float = None) -> bool: - params = locals_to_params(locals()) + async def is_editable( + self, + timeout: Optional[float], + ) -> bool: return await self._frame.is_editable( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) - async def is_enabled(self, timeout: float = None) -> bool: - params = locals_to_params(locals()) + async def is_enabled( + self, + timeout: Optional[float], + ) -> bool: return await self._frame.is_enabled( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) - async def is_hidden(self, timeout: float = None) -> bool: - # timeout is deprecated and does nothing + async def is_hidden( + self, + timeout: Optional[float], # Deprecated; does nothing. + ) -> bool: return await self._frame.is_hidden( self._selector, strict=True, ) - async def is_visible(self, timeout: float = None) -> bool: - # timeout is deprecated and does nothing + async def is_visible( + self, + timeout: Optional[float], # Deprecated; does nothing. + ) -> bool: return await self._frame.is_visible( self._selector, strict=True, @@ -581,64 +693,77 @@ async def is_visible(self, timeout: float = None) -> bool: async def press( self, key: str, - delay: float = None, - timeout: float = None, - noWaitAfter: bool = None, + delay: Optional[float], + timeout: Optional[float], + no_wait_after: Optional[bool], ) -> None: - params = locals_to_params(locals()) - return await self._frame.press(self._selector, strict=True, **params) + await self._frame.press( + self._selector, + key, + strict=True, + delay=delay, + timeout=timeout, + no_wait_after=no_wait_after, + ) async def screenshot( self, - timeout: float = None, - type: Literal["jpeg", "png", "webp"] = None, - path: Union[str, pathlib.Path] = None, - quality: int = None, - omitBackground: bool = None, - animations: Literal["allow", "disabled"] = None, - caret: Literal["hide", "initial"] = None, - scale: Literal["css", "device"] = None, - mask: Sequence["Locator"] = None, - maskColor: str = None, - style: str = None, + timeout: Optional[float], + type: Optional[Literal["jpeg", "png", "webp"]], + path: Optional[Union[str, pathlib.Path]], + quality: Optional[int], + omit_background: Optional[bool], + animations: Optional[Literal["allow", "disabled"]], + caret: Optional[Literal["hide", "initial"]], + scale: Optional[Literal["css", "device"]], + mask: Optional[Sequence["Locator"]], + mask_color: Optional[str], + style: Optional[str], ) -> bytes: - params = locals_to_params(locals()) return await self._with_element( lambda h, timeout: h.screenshot( - **{**params, "timeout": timeout}, + timeout=timeout, + type=type, + path=path, + quality=quality, + omit_background=omit_background, + animations=animations, + caret=caret, + scale=scale, + mask=mask, + mask_color=mask_color, + style=style, ), timeout, ) async def aria_snapshot( self, - timeout: float = None, - depth: int = None, - mode: Literal["ai", "default"] = None, - boxes: bool = None, + timeout: Optional[float], + depth: Optional[int], + mode: Optional[Literal["ai", "default"]], + boxes: Optional[bool], ) -> str: - return await self._frame._channel.send( - "ariaSnapshot", - self._frame._timeout, - { - "selector": self._selector, - **locals_to_params(locals()), - }, + result = await self._frame._channel.ariaSnapshot( + mode=mode, + selector=self._selector, + depth=depth, + boxes=boxes, + timeout=self._frame._timeout(timeout), ) + return result["snapshot"] async def normalize(self) -> "Locator": - result = await self._frame._channel.send_return_as_dict( - "resolveSelector", - None, - {"selector": self._selector, "sdkLanguage": "python"}, + result = await self._frame._channel.resolveSelector( + selector=self._selector, sdkLanguage="python" ) locator = Locator(self._frame, result["resolvedSelector"]) - locator._locator_code = result.get("locatorCode") + locator._locator_code = result["locatorCode"] return locator async def scroll_into_view_if_needed( self, - timeout: float = None, + timeout: Optional[float], ) -> None: return await self._with_element( lambda h, timeout: h.scroll_into_view_if_needed(timeout=timeout), @@ -647,25 +772,33 @@ async def scroll_into_view_if_needed( async def select_option( self, - value: Union[str, Sequence[str]] = None, - index: Union[int, Sequence[int]] = None, - label: Union[str, Sequence[str]] = None, - element: Union["ElementHandle", Sequence["ElementHandle"]] = None, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, + value: Optional[Union[str, Sequence[str]]], + index: Optional[Union[int, Sequence[int]]], + label: Optional[Union[str, Sequence[str]]], + element: Optional[Union["ElementHandle", Sequence["ElementHandle"]]], + timeout: Optional[float], + no_wait_after: Optional[bool], + force: Optional[bool], ) -> List[str]: - params = locals_to_params(locals()) return await self._frame.select_option( self._selector, strict=True, - **params, + value=value, + index=index, + label=label, + element=element, + timeout=timeout, + no_wait_after=no_wait_after, + force=force, ) - async def select_text(self, force: bool = None, timeout: float = None) -> None: - params = locals_to_params(locals()) + async def select_text( + self, + force: Optional[bool], + timeout: Optional[float], + ) -> None: return await self._with_element( - lambda h, timeout: h.select_text(**{**params, "timeout": timeout}), + lambda h, timeout: h.select_text(force=force, timeout=timeout), timeout, ) @@ -678,136 +811,153 @@ async def set_input_files( Sequence[Union[str, pathlib.Path]], Sequence[FilePayload], ], - timeout: float = None, - noWaitAfter: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], ) -> None: - params = locals_to_params(locals()) return await self._frame.set_input_files( self._selector, + files=files, strict=True, - **params, + timeout=timeout, + no_wait_after=no_wait_after, ) async def tap( self, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - params = locals_to_params(locals()) - return await self._frame.tap( + await self._frame.tap( self._selector, strict=True, - **params, + modifiers=modifiers, + position=position, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + trial=trial, + scroll=scroll, ) - async def text_content(self, timeout: float = None) -> Optional[str]: - params = locals_to_params(locals()) + async def text_content( + self, + timeout: Optional[float], + ) -> Optional[str]: return await self._frame.text_content( - self._selector, - strict=True, - **params, + self._selector, strict=True, timeout=timeout ) async def type( self, text: str, - delay: float = None, - timeout: float = None, - noWaitAfter: bool = None, + delay: Optional[float], + timeout: Optional[float], + no_wait_after: Optional[bool], ) -> None: - params = locals_to_params(locals()) - return await self._frame.type( + await self._frame.type( self._selector, + text, strict=True, - **params, + delay=delay, + timeout=timeout, + no_wait_after=no_wait_after, ) async def press_sequentially( self, text: str, - delay: float = None, - timeout: float = None, - noWaitAfter: bool = None, + delay: Optional[float], + timeout: Optional[float], + no_wait_after: Optional[bool], ) -> None: - await self.type(text, delay=delay, timeout=timeout) + await self.type(text, delay=delay, timeout=timeout, no_wait_after=no_wait_after) async def uncheck( self, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - params = locals_to_params(locals()) - return await self._frame.uncheck( + await self._frame.uncheck( self._selector, strict=True, - **params, + position=position, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + trial=trial, + scroll=scroll, ) async def all_inner_texts( self, ) -> List[str]: return await self._frame.eval_on_selector_all( - self._selector, "ee => ee.map(e => e.innerText)" + self._selector, expression="ee => ee.map(e => e.innerText)", arg=None ) async def all_text_contents( self, ) -> List[str]: return await self._frame.eval_on_selector_all( - self._selector, "ee => ee.map(e => e.textContent || '')" + self._selector, + expression="ee => ee.map(e => e.textContent || '')", + arg=None, ) async def wait_for( self, - timeout: float = None, - state: Literal["attached", "detached", "hidden", "visible"] = None, + timeout: Optional[float], + state: Optional[Literal["attached", "detached", "hidden", "visible"]], ) -> None: await self._frame.wait_for_selector( - self._selector, strict=True, timeout=timeout, state=state + self._selector, + strict=True, + timeout=timeout, + state=state, + omit_return_value=True, ) async def wait_for_function( self, expression: str, - arg: Serializable = None, - timeout: float = None, + arg: Serializable, + timeout: Optional[float], ) -> None: - await self._frame._channel.send( - "waitForFunction", - self._frame._timeout, - { - "selector": self._selector, - "strict": True, - "expression": expression, - "arg": serialize_argument(arg), - "timeout": timeout, - }, + await self._frame._channel.waitForFunction( + expression=expression, + isFunction=None, + arg=serialize_argument(arg), + pollingInterval=None, + selector=self._selector, + strict=True, + timeout=self._frame._timeout(timeout), ) async def set_checked( self, checked: bool, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: if checked: await self.check( position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, trial=trial, scroll=scroll, ) @@ -816,6 +966,7 @@ async def set_checked( position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, trial=trial, scroll=scroll, ) @@ -828,7 +979,7 @@ async def _expect( ) -> FrameExpectResult: return await self._frame._expect(self._selector, expression, options, title) - async def highlight(self, style: str = None) -> DisposableStub: + async def highlight(self, style: Optional[str]) -> DisposableStub: await self._frame._highlight(self._selector, style) return DisposableStub(lambda: self.hide_highlight(), self._frame) @@ -858,90 +1009,132 @@ def _nth_selector(self, nth: str) -> str: def locator( self, - selectorOrLocator: Union["Locator", str], - hasText: Union[str, Pattern[str]] = None, - hasNotText: Union[str, Pattern[str]] = None, - has: Locator = None, - hasNot: Locator = None, + selector_or_locator: Union["Locator", str], + has_text: Optional[Union[str, Pattern[str]]], + has_not_text: Optional[Union[str, Pattern[str]]], + has: Optional[Locator], + has_not: Optional[Locator], ) -> Locator: - if isinstance(selectorOrLocator, str): + if isinstance(selector_or_locator, str): return Locator( self._frame, - self._child_selector(selectorOrLocator), - has_text=hasText, - has_not_text=hasNotText, + self._child_selector(selector_or_locator), + has_text=has_text, + has_not_text=has_not_text, has=has, - has_not=hasNot, + has_not=has_not, ) - selectorOrLocator = to_impl(selectorOrLocator) - if selectorOrLocator._frame != self._frame: + selector_or_locator = to_impl(selector_or_locator) + if selector_or_locator._frame != self._frame: raise ValueError("Locators must belong to the same frame.") return Locator( self._frame, - self._child_selector(selectorOrLocator._selector), - has_text=hasText, - has_not_text=hasNotText, + self._child_selector(selector_or_locator._selector), + has_text=has_text, + has_not_text=has_not_text, has=has, - has_not=hasNot, + has_not=has_not, ) def get_by_alt_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_alt_text_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_alt_text_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_label( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_label_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_label_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_placeholder( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_placeholder_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_placeholder_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_role( self, role: AriaRole, - checked: bool = None, - disabled: bool = None, - expanded: bool = None, - includeHidden: bool = None, - level: int = None, - name: Union[str, Pattern[str]] = None, - pressed: bool = None, - selected: bool = None, - exact: bool = None, - description: Union[str, Pattern[str]] = None, + checked: Optional[bool], + disabled: Optional[bool], + expanded: Optional[bool], + include_hidden: Optional[bool], + level: Optional[int], + name: Optional[Union[str, Pattern[str]]], + pressed: Optional[bool], + selected: Optional[bool], + exact: Optional[bool], + description: Optional[Union[str, Pattern[str]]], ) -> "Locator": return self.locator( - get_by_role_selector( + selector_or_locator=get_by_role_selector( role, checked=checked, disabled=disabled, expanded=expanded, - includeHidden=includeHidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, selected=selected, exact=exact, description=description, - ) + ), + has_text=None, + has_not_text=None, + has=None, + has_not=None, ) - def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator": - return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId)) + def get_by_test_id(self, test_id: Union[str, Pattern[str]]) -> "Locator": + return self.locator( + selector_or_locator=get_by_test_id_selector( + test_id_attribute_name(), test_id + ), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_text_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_text_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def get_by_title( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": - return self.locator(get_by_title_selector(text, exact=exact)) + return self.locator( + selector_or_locator=get_by_title_selector(text, exact=exact), + has_text=None, + has_not_text=None, + has=None, + has_not=None, + ) def frame_locator(self, selector: str) -> "FrameLocator": return FrameLocator(self._frame, self._child_selector(selector)) diff --git a/playwright/_impl/_locator_utils.py b/playwright/_impl/_locator_utils.py index 2ed19143a..196a18d44 100644 --- a/playwright/_impl/_locator_utils.py +++ b/playwright/_impl/_locator_utils.py @@ -66,7 +66,7 @@ def get_by_role_selector( checked: bool = None, disabled: bool = None, expanded: bool = None, - includeHidden: bool = None, + include_hidden: bool = None, level: int = None, name: Union[str, Pattern[str]] = None, pressed: bool = None, @@ -83,8 +83,8 @@ def get_by_role_selector( props.append(("selected", bool_to_js_bool(selected))) if expanded is not None: props.append(("expanded", bool_to_js_bool(expanded))) - if includeHidden is not None: - props.append(("include-hidden", bool_to_js_bool(includeHidden))) + if include_hidden is not None: + props.append(("include-hidden", bool_to_js_bool(include_hidden))) if level is not None: props.append(("level", str(level))) if name is not None: diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py index 3628df6df..61bb3d444 100644 --- a/playwright/_impl/_network.py +++ b/playwright/_impl/_network.py @@ -28,9 +28,8 @@ Dict, List, Optional, - Pattern, Sequence, - TypedDict, + Tuple, Union, cast, ) @@ -57,7 +56,7 @@ URLMatch, WebSocketRouteHandlerCallback, async_readfile, - locals_to_params, + create_task_and_ignore_exception, url_matches, ) from playwright._impl._internal_structures import Headers, HeadersArray @@ -65,17 +64,18 @@ from playwright._impl._waiter import Waiter if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._browser_context import BrowserContext from playwright._impl._fetch import APIResponse from playwright._impl._frame import Frame from playwright._impl._page import Page, Worker - - -class FallbackOverrideParameters(TypedDict, total=False): - url: Optional[str] - method: Optional[str] - headers: Optional[Dict[str, str]] - postData: Optional[Union[str, bytes]] + from playwright._impl._protocol_channels import ( + RequestChannel, + ResponseChannel, + RouteChannel, + WebSocketChannel, + WebSocketRouteChannel, + ) class SerializedFallbackOverrides: @@ -134,31 +134,9 @@ def to_http_credentials_protocol( ] or None -def to_clear_cookies_params( - name: Optional[Union[str, Pattern[str]]], - domain: Optional[Union[str, Pattern[str]]], - path: Optional[Union[str, Pattern[str]]], -) -> Dict[str, Optional[str]]: - return { - "name": name if isinstance(name, str) else None, - "nameRegexSource": name.pattern if isinstance(name, Pattern) else None, - "nameRegexFlags": ( - escape_regex_flags(name) if isinstance(name, Pattern) else None - ), - "domain": domain if isinstance(domain, str) else None, - "domainRegexSource": (domain.pattern if isinstance(domain, Pattern) else None), - "domainRegexFlags": ( - escape_regex_flags(domain) if isinstance(domain, Pattern) else None - ), - "path": path if isinstance(path, str) else None, - "pathRegexSource": path.pattern if isinstance(path, Pattern) else None, - "pathRegexFlags": ( - escape_regex_flags(path) if isinstance(path, Pattern) else None - ), - } - - class Request(ChannelOwner): + _channel: "RequestChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -191,17 +169,22 @@ def __init__( def __repr__(self) -> str: return f"" - def _apply_fallback_overrides(self, overrides: FallbackOverrideParameters) -> None: - self._fallback_overrides.url = overrides.get( - "url", self._fallback_overrides.url - ) - self._fallback_overrides.method = overrides.get( - "method", self._fallback_overrides.method - ) - self._fallback_overrides.headers = overrides.get( - "headers", self._fallback_overrides.headers - ) - post_data = overrides.get("postData") + def _apply_fallback_overrides( + self, + url: Optional[str], + method: Optional[str], + headers: Optional[Dict[str, str]], + post_data: Optional[Union[Any, str, bytes]], + ) -> None: + if url is not None: + self._fallback_overrides.url = url + if method is not None: + self._fallback_overrides.method = method + if headers is not None: + # A None value removes the header. + self._fallback_overrides.headers = { + name: value for name, value in headers.items() if value is not None + } if isinstance(post_data, str): self._fallback_overrides.post_data_buffer = post_data.encode() elif isinstance(post_data, bytes): @@ -232,10 +215,7 @@ async def sizes(self) -> RequestSizes: response = await self.response() if not response: raise Error("Unable to fetch sizes for failed request") - return await response._channel.send( - "sizes", - None, - ) + return (await response._channel.sizes())["sizes"] @property def post_data(self) -> Optional[str]: @@ -269,12 +249,7 @@ def post_data_buffer(self) -> Optional[bytes]: return None async def response(self) -> Optional["Response"]: - return from_nullable_channel( - await self._channel.send( - "response", - None, - ) - ) + return from_nullable_channel((await self._channel.response())["response"]) @property def existing_response(self) -> Optional["Response"]: @@ -343,10 +318,10 @@ async def _actual_headers(self) -> "RawHeaders": return RawHeaders(serialize_headers(override)) if not self._all_headers_future: self._all_headers_future = asyncio.Future() - headers = await self._channel.send( - "rawRequestHeaders", None, is_internal=True + result = await self._connection.wrap_api_call( + lambda: self._channel.rawRequestHeaders(), is_internal=True ) - self._all_headers_future.set_result(RawHeaders(headers)) + self._all_headers_future.set_result(RawHeaders(result["headers"])) return await self._all_headers_future def _target_closed_future(self) -> asyncio.Future: @@ -368,6 +343,8 @@ def _safe_page(self) -> "Optional[Page]": class Route(ChannelOwner): + _channel: "RouteChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -397,88 +374,86 @@ def __repr__(self) -> str: def request(self) -> Request: return from_channel(self._initializer["request"]) - async def abort(self, errorCode: str = None) -> None: + async def abort(self, error_code: Optional[str]) -> None: await self._handle_route( lambda: self._race_with_page_close( - self._channel.send( - "abort", - None, - { - "errorCode": errorCode, - }, - ) + self._channel.abort(errorCode=error_code) ) ) async def fulfill( self, - status: int = None, - headers: Dict[str, str] = None, - body: Union[str, bytes] = None, - json: Any = None, - path: Union[str, Path] = None, - contentType: str = None, - response: "APIResponse" = None, + status: Optional[int], + headers: Optional[Dict[str, str]], + body: Optional[Union[str, bytes]], + json: Optional[Any], + path: Optional[Union[str, Path]], + content_type: Optional[str], + response: Optional["APIResponse"], ) -> None: await self._handle_route( lambda: self._inner_fulfill( - status, headers, body, json, path, contentType, response + status=status, + headers=headers, + body=body, + json=json, + path=path, + content_type=content_type, + response=response, ) ) async def _inner_fulfill( self, - status: int = None, - headers: Dict[str, str] = None, - body: Union[str, bytes] = None, - json: Any = None, - path: Union[str, Path] = None, - contentType: str = None, - response: "APIResponse" = None, + status: Optional[int], + headers: Optional[Dict[str, str]], + body: Optional[Union[str, bytes]], + json: Optional[Any], + path: Optional[Union[str, Path]], + content_type: Optional[str], + response: Optional["APIResponse"], ) -> None: - params = locals_to_params(locals()) - # These options become the body and the headers below. - for name in ("json", "path", "contentType", "response"): - params.pop(name, None) - if json is not None: if body is not None: raise Error("Can specify either body or json parameters") body = json_utils.dumps(json) + if headers is not None: + # A None value removes the header. + headers = {k: v for k, v in headers.items() if v is not None} + + fetch_response_uid: Optional[str] = None if response: - params["status"] = ( - params["status"] if params.get("status") else response.status - ) - params["headers"] = ( - params["headers"] if params.get("headers") else response.headers - ) + status = status if status else response.status + headers = headers if headers else response.headers from playwright._impl._fetch import APIResponse if body is None and path is None and isinstance(response, APIResponse): if response._request._connection is self._connection: - params["fetchResponseUid"] = response._fetch_uid + fetch_response_uid = response._fetch_uid else: body = await response.body() length = 0 + encoded_body: Optional[str] = None + is_base64: Optional[bool] = None if isinstance(body, str): - params["body"] = body - params["isBase64"] = False + encoded_body = body + is_base64 = False length = len(body.encode()) elif isinstance(body, bytes): - params["body"] = base64.b64encode(body).decode() - params["isBase64"] = True + encoded_body = base64.b64encode(body).decode() + is_base64 = True length = len(body) elif path: file_content = Path(path).read_bytes() - params["body"] = base64.b64encode(file_content).decode() - params["isBase64"] = True + encoded_body = base64.b64encode(file_content).decode() + is_base64 = True length = len(file_content) - headers = {k.lower(): str(v) for k, v in params.get("headers", {}).items()} - if contentType: - headers["content-type"] = contentType + headers = {k.lower(): str(v) for k, v in (headers or {}).items()} + if content_type: + headers["content-type"] = content_type elif json or isinstance(json, (list, tuple, dict)): # Like the JS client, which checks truthiness: [] and {} are truthy in JS. headers["content-type"] = "application/json" @@ -488,9 +463,16 @@ async def _inner_fulfill( ) if length and "content-length" not in headers: headers["content-length"] = str(length) - params["headers"] = serialize_headers(headers) - await self._race_with_page_close(self._channel.send("fulfill", None, params)) + await self._race_with_page_close( + self._channel.fulfill( + status=status, + headers=serialize_headers(headers), + body=encoded_body, + isBase64=is_base64, + fetchResponseUid=fetch_response_uid, + ) + ) async def _handle_route(self, callback: Callable) -> None: self._check_not_handled() @@ -503,50 +485,56 @@ async def _handle_route(self, callback: Callable) -> None: async def fetch( self, - url: str = None, - method: str = None, - headers: Dict[str, str] = None, - postData: Union[Any, str, bytes] = None, - maxRedirects: int = None, - maxRetries: int = None, - timeout: float = None, + url: Optional[str], + method: Optional[str], + headers: Optional[Dict[str, str]], + post_data: Optional[Union[Any, str, bytes]], + max_redirects: Optional[int], + max_retries: Optional[int], + timeout: Optional[float], ) -> "APIResponse": return await self._connection.wrap_api_call( lambda: self._context.request._inner_fetch( - self.request, - url, - method, - headers, - postData, - maxRedirects=maxRedirects, - maxRetries=maxRetries, + request=self.request, + url=url, + method=method, + headers=headers, + data=post_data, + params=None, + form=None, + multipart=None, timeout=timeout, + fail_on_status_code=None, + ignore_https_errors=None, + max_redirects=max_redirects, + max_retries=max_retries, ) ) async def fallback( self, - url: str = None, - method: str = None, - headers: Dict[str, str] = None, - postData: Union[Any, str, bytes] = None, + url: Optional[str], + method: Optional[str], + headers: Optional[Dict[str, str]], + post_data: Optional[Union[Any, str, bytes]], ) -> None: - overrides = cast(FallbackOverrideParameters, locals_to_params(locals())) self._check_not_handled() - self.request._apply_fallback_overrides(overrides) + self.request._apply_fallback_overrides( + url=url, method=method, headers=headers, post_data=post_data + ) self._report_handled(False) async def continue_( self, - url: str = None, - method: str = None, - headers: Dict[str, str] = None, - postData: Union[Any, str, bytes] = None, + url: Optional[str], + method: Optional[str], + headers: Optional[Dict[str, str]], + post_data: Optional[Union[Any, str, bytes]], ) -> None: - overrides = cast(FallbackOverrideParameters, locals_to_params(locals())) - async def _inner() -> None: - self.request._apply_fallback_overrides(overrides) + self.request._apply_fallback_overrides( + url=url, method=method, headers=headers, post_data=post_data + ) await self._inner_continue(False) return await self._handle_route(_inner) @@ -554,29 +542,25 @@ async def _inner() -> None: async def _inner_continue(self, is_fallback: bool = False) -> None: options = self.request._fallback_overrides await self._race_with_page_close( - self._channel.send( - "continue", - None, - { - "url": options.url, - "method": options.method, - "headers": ( - serialize_headers(options.headers) if options.headers else None - ), - "postData": ( - base64.b64encode(options.post_data_buffer).decode() - if options.post_data_buffer is not None - else None - ), - "isFallback": is_fallback, - }, + self._channel.continue_( + url=options.url, + method=options.method, + headers=( + serialize_headers(options.headers) if options.headers else None + ), + postData=( + base64.b64encode(options.post_data_buffer).decode() + if options.post_data_buffer is not None + else None + ), + isFallback=is_fallback, ) ) async def _redirected_navigation_request(self, url: str) -> None: await self._handle_route( lambda: self._race_with_page_close( - self._channel.send("redirectNavigationRequest", None, {"url": url}) + self._channel.redirectNavigationRequest(url=url) ) ) @@ -623,31 +607,29 @@ def url(self) -> str: def protocols(self) -> List[str]: return list(self._ws._initializer.get("protocols", [])) - def close(self, code: int = None, reason: str = None) -> None: - self._ws._channel.send_may_fail( - "closeServer", - None, - { - "code": code, - "reason": reason, - "wasClean": True, - }, + def close(self, code: Optional[int], reason: Optional[str]) -> None: + create_task_and_ignore_exception( + self._ws._loop, + self._ws._channel.closeServer(code=code, reason=reason, wasClean=True), ) def send(self, message: Union[str, bytes]) -> None: - if isinstance(message, str): - self._ws._channel.send_may_fail( - "sendToServer", None, {"message": message, "isBase64": False} - ) - else: - self._ws._channel.send_may_fail( - "sendToServer", - None, - {"message": base64.b64encode(message).decode(), "isBase64": True}, - ) + text, is_base64 = _encode_web_socket_message(message) + create_task_and_ignore_exception( + self._ws._loop, + self._ws._channel.sendToServer(message=text, isBase64=is_base64), + ) + + +def _encode_web_socket_message(message: Union[str, bytes]) -> Tuple[str, bool]: + if isinstance(message, str): + return message, False + return base64.b64encode(message).decode(), True class WebSocketRoute(ChannelOwner): + _channel: "WebSocketRouteChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -663,12 +645,14 @@ def __init__( self._server = ServerWebSocketRoute(self) self._connected = False - self._channel.on("messageFromPage", self._channel_message_from_page) - self._channel.on("messageFromServer", self._channel_message_from_server) - self._channel.on("closePage", self._channel_close_page) - self._channel.on("closeServer", self._channel_close_server) + self._channel.onMessageFromPage(self._channel_message_from_page) + self._channel.onMessageFromServer(self._channel_message_from_server) + self._channel.onClosePage(self._channel_close_page) + self._channel.onCloseServer(self._channel_close_server) - def _channel_message_from_page(self, event: Dict) -> None: + def _channel_message_from_page( + self, event: "channels.WebSocketRouteMessageFromPageEvent" + ) -> None: if self._on_page_message: self._on_page_message( base64.b64decode(event["message"]) @@ -676,9 +660,16 @@ def _channel_message_from_page(self, event: Dict) -> None: else event["message"] ) elif self._connected: - self._channel.send_may_fail("sendToServer", None, event) + create_task_and_ignore_exception( + self._loop, + self._channel.sendToServer( + message=event["message"], isBase64=event["isBase64"] + ), + ) - def _channel_message_from_server(self, event: Dict) -> None: + def _channel_message_from_server( + self, event: "channels.WebSocketRouteMessageFromServerEvent" + ) -> None: if self._on_server_message: self._on_server_message( base64.b64decode(event["message"]) @@ -686,19 +677,42 @@ def _channel_message_from_server(self, event: Dict) -> None: else event["message"] ) else: - self._channel.send_may_fail("sendToPage", None, event) + create_task_and_ignore_exception( + self._loop, + self._channel.sendToPage( + message=event["message"], isBase64=event["isBase64"] + ), + ) - def _channel_close_page(self, event: Dict) -> None: + def _channel_close_page( + self, event: "channels.WebSocketRouteClosePageEvent" + ) -> None: if self._on_page_close: self._on_page_close(event["code"], event["reason"]) else: - self._channel.send_may_fail("closeServer", None, event) + create_task_and_ignore_exception( + self._loop, + self._channel.closeServer( + code=event["code"], + reason=event["reason"], + wasClean=event["wasClean"], + ), + ) - def _channel_close_server(self, event: Dict) -> None: + def _channel_close_server( + self, event: "channels.WebSocketRouteCloseServerEvent" + ) -> None: if self._on_server_close: self._on_server_close(event["code"], event["reason"]) else: - self._channel.send_may_fail("closePage", None, event) + create_task_and_ignore_exception( + self._loop, + self._channel.closePage( + code=event["code"], + reason=event["reason"], + wasClean=event["wasClean"], + ), + ) @property def url(self) -> str: @@ -708,11 +722,9 @@ def url(self) -> str: def protocols(self) -> List[str]: return list(self._initializer.get("protocols", [])) - async def close(self, code: int = None, reason: str = None) -> None: + async def close(self, code: Optional[int], reason: Optional[str]) -> None: try: - await self._channel.send( - "closePage", None, {"code": code, "reason": reason, "wasClean": True} - ) + await self._channel.closePage(code=code, reason=reason, wasClean=True) except Exception: pass @@ -720,23 +732,14 @@ def connect_to_server(self) -> "WebSocketRoute": if self._connected: raise Error("Already connected to the server") self._connected = True - self._channel.send_may_fail("connect", None) + create_task_and_ignore_exception(self._loop, self._channel.connect()) return cast("WebSocketRoute", self._server) def send(self, message: Union[str, bytes]) -> None: - if isinstance(message, str): - self._channel.send_may_fail( - "sendToPage", None, {"message": message, "isBase64": False} - ) - else: - self._channel.send_may_fail( - "sendToPage", - None, - { - "message": base64.b64encode(message).decode(), - "isBase64": True, - }, - ) + text, is_base64 = _encode_web_socket_message(message) + create_task_and_ignore_exception( + self._loop, self._channel.sendToPage(message=text, isBase64=is_base64) + ) def on_message(self, handler: Callable[[Union[str, bytes]], Any]) -> None: self._on_page_message = handler @@ -749,10 +752,7 @@ async def _after_handle(self) -> None: return # Ensure that websocket is "open" and can send messages without an actual server connection. try: - await self._channel.send( - "ensureOpened", - None, - ) + await self._channel.ensureOpened() except Exception: pass @@ -771,8 +771,8 @@ def __init__( @staticmethod def prepare_interception_patterns( handlers: List["WebSocketRouteHandler"], - ) -> List[dict]: - patterns = [] + ) -> List["channels.PageSetWebSocketInterceptionPatternsParamsPatterns"]: + patterns: List[channels.PageSetWebSocketInterceptionPatternsParamsPatterns] = [] all_urls = False for handler in handlers: if isinstance(handler.url, str): @@ -802,6 +802,8 @@ async def handle(self, websocket_route: "WebSocketRoute") -> None: class Response(ChannelOwner): + _channel: "ResponseChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -868,33 +870,20 @@ async def header_values(self, name: str) -> List[str]: async def _actual_headers(self) -> "RawHeaders": if not self._raw_headers_future: self._raw_headers_future = asyncio.Future() - headers = cast( - HeadersArray, - await self._channel.send( - "rawResponseHeaders", - None, - ), - ) + headers = (await self._channel.rawResponseHeaders())["headers"] self._raw_headers_future.set_result(RawHeaders(headers)) return await self._raw_headers_future async def server_addr(self) -> Optional[RemoteAddr]: - return await self._channel.send( - "serverAddr", - None, - ) + return (await self._channel.serverAddr())["value"] async def security_details(self) -> Optional[SecurityDetails]: - return await self._channel.send( - "securityDetails", - None, + return cast( + Optional[SecurityDetails], (await self._channel.securityDetails())["value"] ) async def http_version(self) -> str: - return await self._channel.send( - "httpVersion", - None, - ) + return (await self._channel.httpVersion())["value"] async def finished(self) -> None: async def on_finished() -> None: @@ -913,11 +902,7 @@ async def on_finished() -> None: await on_finished_task async def body(self) -> bytes: - binary = await self._channel.send( - "body", - None, - ) - return base64.b64decode(binary) + return base64.b64decode((await self._channel.body())["binary"]) async def text(self) -> str: content = await self.body() @@ -936,6 +921,8 @@ def frame(self) -> "Frame": class WebSocket(ChannelOwner): + _channel: "WebSocketChannel" + Events = SimpleNamespace( Close="close", FrameReceived="framereceived", @@ -949,19 +936,16 @@ def __init__( super().__init__(parent, type, guid, initializer) self._is_closed = False self._page = cast("Page", parent) - self._channel.on( - "frameSent", - lambda params: self._on_frame_sent(params["opcode"], params["data"]), + self._channel.onFrameSent( + lambda event: self._on_frame_sent(event["opcode"], event["data"]) ) - self._channel.on( - "frameReceived", - lambda params: self._on_frame_received(params["opcode"], params["data"]), + self._channel.onFrameReceived( + lambda event: self._on_frame_received(event["opcode"], event["data"]) ) - self._channel.on( - "socketError", - lambda params: self.emit(WebSocket.Events.Error, params["error"]), + self._channel.onSocketError( + lambda event: self.emit(WebSocket.Events.Error, event["error"]) ) - self._channel.on("close", lambda params: self._on_close()) + self._channel.onClose(self._on_close) def __repr__(self) -> str: return f"" @@ -973,8 +957,8 @@ def url(self) -> str: def expect_event( self, event: str, - predicate: Callable = None, - timeout: float = None, + predicate: Optional[Callable], + timeout: Optional[float], ) -> EventContextManagerImpl: if timeout is None: timeout = cast(Any, self._parent)._timeout_settings.timeout() @@ -994,9 +978,11 @@ def expect_event( return EventContextManagerImpl(waiter.result()) async def wait_for_event( - self, event: str, predicate: Callable = None, timeout: float = None + self, event: str, predicate: Optional[Callable], timeout: Optional[float] ) -> Any: - async with self.expect_event(event, predicate, timeout) as event_info: + async with self.expect_event( + event=event, predicate=predicate, timeout=timeout + ) as event_info: pass return await event_info diff --git a/playwright/_impl/_page.py b/playwright/_impl/_page.py index bd4f359ee..bcc99d8d5 100644 --- a/playwright/_impl/_page.py +++ b/playwright/_impl/_page.py @@ -83,7 +83,7 @@ WebSocketRouteHandlerCallback, async_readfile, async_writefile, - locals_to_params, + create_task_and_ignore_exception, make_dirs_for_file, url_matches, ) @@ -112,10 +112,16 @@ from playwright._impl._web_storage import WebStorage if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._browser_context import BrowserContext from playwright._impl._fetch import APIRequestContext from playwright._impl._locator import FrameLocator, Locator from playwright._impl._network import WebSocket + from playwright._impl._protocol_channels import ( + BindingCallChannel, + PageChannel, + WorkerChannel, + ) class LocatorHandler: @@ -138,6 +144,8 @@ def __call__(self) -> Any: class Page(ChannelOwner): + _channel: "PageChannel" + Events = SimpleNamespace( Close="close", Crash="crash", @@ -197,59 +205,49 @@ def __init__( self._close_reason: Optional[str] = None self._close_was_called = False self._har_routers: List[HarRouter] = [] - self._locator_handlers: Dict[str, LocatorHandler] = {} - - self._channel.on( - "bindingCall", - lambda params: self._on_binding(from_channel(params["binding"])), - ) - self._channel.on("close", lambda _: self._on_close()) - self._channel.on("crash", lambda _: self._on_crash()) - self._channel.on("download", lambda params: self._on_download(params)) - self._channel.on( - "fileChooser", - lambda params: self.emit( + self._locator_handlers: Dict[int, LocatorHandler] = {} + + self._channel.onBindingCall( + lambda event: self._on_binding(from_channel(event["binding"])) + ) + self._channel.onClose(lambda: self._on_close()) + self._channel.onCrash(lambda: self._on_crash()) + self._channel.onDownload(self._on_download) + self._channel.onFileChooser( + lambda event: self.emit( Page.Events.FileChooser, - FileChooser( - self, from_channel(params["element"]), params["isMultiple"] - ), - ), + FileChooser(self, from_channel(event["element"]), event["isMultiple"]), + ) ) - self._channel.on( - "frameAttached", - lambda params: self._on_frame_attached(from_channel(params["frame"])), + self._channel.onFrameAttached( + lambda event: self._on_frame_attached(from_channel(event["frame"])) ) - self._channel.on( - "frameDetached", - lambda params: self._on_frame_detached(from_channel(params["frame"])), + self._channel.onFrameDetached( + lambda event: self._on_frame_detached(from_channel(event["frame"])) ) - self._channel.on( - "locatorHandlerTriggered", - lambda params: self._loop.create_task( - self._on_locator_handler_triggered(params["uid"]) - ), + self._channel.onLocatorHandlerTriggered( + lambda event: self._loop.create_task( + self._on_locator_handler_triggered(event["uid"]) + ) ) - self._channel.on( - "route", - lambda params: self._loop.create_task( - self._on_route(from_channel(params["route"])) - ), + self._channel.onRoute( + lambda event: self._loop.create_task( + self._on_route(from_channel(event["route"])) + ) ) - self._channel.on( - "webSocketRoute", - lambda params: self._loop.create_task( - self._on_web_socket_route(from_channel(params["webSocketRoute"])) - ), + self._channel.onWebSocketRoute( + lambda event: self._loop.create_task( + self._on_web_socket_route(from_channel(event["webSocketRoute"])) + ) ) - self._channel.on("viewportSizeChanged", self._on_viewport_size_changed) - self._channel.on( - "webSocket", - lambda params: self.emit( - Page.Events.WebSocket, from_channel(params["webSocket"]) - ), + self._channel.onViewportSizeChanged(self._on_viewport_size_changed) + self._channel.onWebSocket( + lambda event: self.emit( + Page.Events.WebSocket, from_channel(event["webSocket"]) + ) ) - self._channel.on( - "worker", lambda params: self._on_worker(from_channel(params["worker"])) + self._channel.onWorker( + lambda event: self._on_worker(from_channel(event["worker"])) ) self._closed_or_crashed_future: asyncio.Future = asyncio.Future() self.on( @@ -368,16 +366,18 @@ def _on_close(self) -> None: def _on_crash(self) -> None: self.emit(Page.Events.Crash, self) - def _on_download(self, params: Any) -> None: - url = params["url"] - suggested_filename = params["suggestedFilename"] - artifact = cast(Artifact, from_channel(params["artifact"])) + def _on_download(self, event: "channels.PageDownloadEvent") -> None: + url = event["url"] + suggested_filename = event["suggestedFilename"] + artifact = cast(Artifact, from_channel(event["artifact"])) download = Download(self, url, suggested_filename, artifact) self.emit(Page.Events.Download, download) self._browser_context.emit("download", download) - def _on_viewport_size_changed(self, params: Any) -> None: - self._viewport_size = params["viewportSize"] + def _on_viewport_size_changed( + self, event: "channels.PageViewportSizeChangedEvent" + ) -> None: + self._viewport_size = event["viewportSize"] @property def context(self) -> "BrowserContext": @@ -396,7 +396,7 @@ async def opener(self) -> Optional["Page"]: def main_frame(self) -> Frame: return self._main_frame - def frame(self, name: str = None, url: URLMatch = None) -> Optional[Frame]: + def frame(self, name: Optional[str], url: Optional[URLMatch]) -> Optional[Frame]: for frame in self._frames: if name and frame.name == name: return frame @@ -418,9 +418,9 @@ def set_default_timeout(self, timeout: float) -> None: async def query_selector( self, selector: str, - strict: bool = None, + strict: Optional[bool], ) -> Optional[ElementHandle]: - return await self._main_frame.query_selector(selector, strict) + return await self._main_frame.query_selector(selector, strict=strict) async def query_selector_all(self, selector: str) -> List[ElementHandle]: return await self._main_frame.query_selector_all(selector) @@ -428,97 +428,138 @@ async def query_selector_all(self, selector: str) -> List[ElementHandle]: async def wait_for_selector( self, selector: str, - timeout: float = None, - state: Literal["attached", "detached", "hidden", "visible"] = None, - strict: bool = None, + timeout: Optional[float], + state: Optional[Literal["attached", "detached", "hidden", "visible"]], + strict: Optional[bool], ) -> Optional[ElementHandle]: - return await self._main_frame.wait_for_selector(**locals_to_params(locals())) + return await self._main_frame.wait_for_selector( + selector, timeout=timeout, state=state, strict=strict + ) async def is_checked( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._main_frame.is_checked(**locals_to_params(locals())) + return await self._main_frame.is_checked( + selector, strict=strict, timeout=timeout + ) async def is_disabled( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._main_frame.is_disabled(**locals_to_params(locals())) + return await self._main_frame.is_disabled( + selector, strict=strict, timeout=timeout + ) async def is_editable( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._main_frame.is_editable(**locals_to_params(locals())) + return await self._main_frame.is_editable( + selector, strict=strict, timeout=timeout + ) async def is_enabled( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> bool: - return await self._main_frame.is_enabled(**locals_to_params(locals())) + return await self._main_frame.is_enabled( + selector, strict=strict, timeout=timeout + ) async def is_hidden( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], # Deprecated; does nothing. ) -> bool: - # timeout is deprecated and does nothing - return await self._main_frame.is_hidden(selector=selector, strict=strict) + return await self._main_frame.is_hidden(selector, strict=strict) async def is_visible( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], # Deprecated; does nothing. ) -> bool: - # timeout is deprecated and does nothing - return await self._main_frame.is_visible(selector=selector, strict=strict) + return await self._main_frame.is_visible(selector, strict=strict) async def dispatch_event( self, selector: str, type: str, - eventInit: Dict = None, - timeout: float = None, - strict: bool = None, + event_init: Optional[Dict], + timeout: Optional[float], + strict: Optional[bool], ) -> None: - return await self._main_frame.dispatch_event(**locals_to_params(locals())) + await self._main_frame.dispatch_event( + selector, + type, + event_init=event_init, + timeout=timeout, + strict=strict, + ) - async def evaluate(self, expression: str, arg: Serializable = None) -> Any: - return await self._main_frame.evaluate(expression, arg) + async def evaluate(self, expression: str, arg: Optional[Serializable]) -> Any: + return await self._main_frame.evaluate(expression, arg=arg) async def evaluate_handle( - self, expression: str, arg: Serializable = None + self, expression: str, arg: Optional[Serializable] ) -> JSHandle: - return await self._main_frame.evaluate_handle(expression, arg) + return await self._main_frame.evaluate_handle(expression, arg=arg) async def eval_on_selector( self, selector: str, expression: str, - arg: Serializable = None, - strict: bool = None, + arg: Optional[Serializable], + strict: Optional[bool], ) -> Any: return await self._main_frame.eval_on_selector( - selector, expression, arg, strict + selector, expression, arg=arg, strict=strict ) async def eval_on_selector_all( self, selector: str, expression: str, - arg: Serializable = None, + arg: Optional[Serializable], ) -> Any: - return await self._main_frame.eval_on_selector_all(selector, expression, arg) + return await self._main_frame.eval_on_selector_all( + selector, expression, arg=arg + ) async def add_script_tag( self, - url: str = None, - path: Union[str, Path] = None, - content: str = None, - type: str = None, + url: Optional[str], + path: Optional[Union[str, Path]], + content: Optional[str], + type: Optional[str], ) -> ElementHandle: - return await self._main_frame.add_script_tag(**locals_to_params(locals())) + return await self._main_frame.add_script_tag( + url=url, path=path, content=content, type=type + ) async def add_style_tag( - self, url: str = None, path: Union[str, Path] = None, content: str = None + self, + url: Optional[str], + path: Optional[Union[str, Path]], + content: Optional[str], ) -> ElementHandle: - return await self._main_frame.add_style_tag(**locals_to_params(locals())) + return await self._main_frame.add_style_tag(url=url, path=path, content=content) async def expose_function(self, name: str, callback: Callable) -> Disposable: - return await self.expose_binding(name, lambda source, *args: callback(*args)) + return await self.expose_binding( + name, callback=lambda source, *args: callback(*args) + ) async def expose_binding(self, name: str, callback: Callable) -> Disposable: if name in self._bindings: @@ -529,157 +570,132 @@ async def expose_binding(self, name: str, callback: Callable) -> Disposable: ) self._bindings[name] = callback return from_channel( - await self._channel.send( - "exposeBinding", - None, - dict(name=name), - ) + (await self._channel.exposeBinding(name=name, noGlobal=None))["disposable"] ) async def set_extra_http_headers(self, headers: Dict[str, str]) -> None: - await self._channel.send( - "setExtraHTTPHeaders", - None, - dict(headers=serialize_headers(headers)), - ) + await self._channel.setExtraHTTPHeaders(headers=serialize_headers(headers)) @property def url(self) -> str: return self._main_frame.url - async def content(self, includeShadow: bool = None) -> str: - return await self._main_frame.content(includeShadow=includeShadow) + async def content(self, include_shadow: Optional[bool]) -> str: + return await self._main_frame.content(include_shadow=include_shadow) async def set_content( self, html: str, - timeout: float = None, - waitUntil: DocumentLoadState = None, + timeout: Optional[float], + wait_until: Optional[DocumentLoadState], ) -> None: - return await self._main_frame.set_content(**locals_to_params(locals())) + await self._main_frame.set_content(html, timeout=timeout, wait_until=wait_until) async def goto( self, url: str, - timeout: float = None, - waitUntil: DocumentLoadState = None, - referer: str = None, + timeout: Optional[float], + wait_until: Optional[DocumentLoadState], + referer: Optional[str], ) -> Optional[Response]: - return await self._main_frame.goto(**locals_to_params(locals())) + return await self._main_frame.goto( + url, timeout=timeout, wait_until=wait_until, referer=referer + ) async def reload( self, - timeout: float = None, - waitUntil: DocumentLoadState = None, + timeout: Optional[float], + wait_until: Optional[DocumentLoadState], ) -> Optional[Response]: - return from_nullable_channel( - await self._channel.send( - "reload", - self._timeout_settings.navigation_timeout, - locals_to_params(locals()), - ) + result = await self._channel.reload( + waitUntil=wait_until, + timeout=self._timeout_settings.navigation_timeout(timeout), ) + return from_nullable_channel(result["response"]) async def wait_for_load_state( self, - state: Literal["domcontentloaded", "load", "networkidle"] = None, - timeout: float = None, + state: Optional[Literal["domcontentloaded", "load", "networkidle"]], + timeout: Optional[float], ) -> None: - return await self._main_frame.wait_for_load_state(**locals_to_params(locals())) + await self._main_frame.wait_for_load_state(state=state, timeout=timeout) async def wait_for_url( self, url: URLMatch, - waitUntil: DocumentLoadState = None, - timeout: float = None, + wait_until: Optional[DocumentLoadState], + timeout: Optional[float], ) -> None: - return await self._main_frame.wait_for_url(**locals_to_params(locals())) + await self._main_frame.wait_for_url(url, wait_until=wait_until, timeout=timeout) async def wait_for_event( - self, event: str, predicate: Callable = None, timeout: float = None + self, event: str, predicate: Optional[Callable], timeout: Optional[float] ) -> Any: - async with self.expect_event(event, predicate, timeout) as event_info: + async with self.expect_event( + event, predicate=predicate, timeout=timeout + ) as event_info: pass return await event_info async def go_back( self, - timeout: float = None, - waitUntil: DocumentLoadState = None, + timeout: Optional[float], + wait_until: Optional[DocumentLoadState], ) -> Optional[Response]: - return from_nullable_channel( - await self._channel.send( - "goBack", - self._timeout_settings.navigation_timeout, - locals_to_params(locals()), - ) + result = await self._channel.goBack( + waitUntil=wait_until, + timeout=self._timeout_settings.navigation_timeout(timeout), ) + return from_nullable_channel(result["response"]) async def go_forward( self, - timeout: float = None, - waitUntil: DocumentLoadState = None, + timeout: Optional[float], + wait_until: Optional[DocumentLoadState], ) -> Optional[Response]: - return from_nullable_channel( - await self._channel.send( - "goForward", - self._timeout_settings.navigation_timeout, - locals_to_params(locals()), - ) + result = await self._channel.goForward( + waitUntil=wait_until, + timeout=self._timeout_settings.navigation_timeout(timeout), ) + return from_nullable_channel(result["response"]) async def request_gc(self) -> None: - await self._channel.send("requestGC", None) + await self._channel.requestGC() async def emulate_media( self, - media: Literal["null", "print", "screen"] = None, - colorScheme: ColorScheme = None, - reducedMotion: ReducedMotion = None, - forcedColors: ForcedColors = None, - contrast: Contrast = None, + media: Optional[Literal["null", "print", "screen"]], + color_scheme: Optional[ColorScheme], + reduced_motion: Optional[ReducedMotion], + forced_colors: Optional[ForcedColors], + contrast: Optional[Contrast], ) -> None: - params = locals_to_params(locals()) - if "media" in params: - params["media"] = "no-override" if params["media"] == "null" else media - if "colorScheme" in params: - params["colorScheme"] = ( - "no-override" if params["colorScheme"] == "null" else colorScheme - ) - if "reducedMotion" in params: - params["reducedMotion"] = ( - "no-override" if params["reducedMotion"] == "null" else reducedMotion - ) - if "forcedColors" in params: - params["forcedColors"] = ( - "no-override" if params["forcedColors"] == "null" else forcedColors - ) - if "contrast" in params: - params["contrast"] = ( - "no-override" if params["contrast"] == "null" else contrast - ) - await self._channel.send("emulateMedia", None, params) - - async def set_viewport_size(self, viewportSize: ViewportSize) -> None: - self._viewport_size = viewportSize - await self._channel.send( - "setViewportSize", - None, - locals_to_params(locals()), + await self._channel.emulateMedia( + media="no-override" if media == "null" else media, + colorScheme="no-override" if color_scheme == "null" else color_scheme, + reducedMotion=( + "no-override" if reduced_motion == "null" else reduced_motion + ), + forcedColors="no-override" if forced_colors == "null" else forced_colors, + contrast="no-override" if contrast == "null" else contrast, ) + async def set_viewport_size(self, viewport_size: ViewportSize) -> None: + self._viewport_size = viewport_size + await self._channel.setViewportSize(viewportSize=viewport_size) + @property def viewport_size(self) -> Optional[ViewportSize]: return self._viewport_size async def bring_to_front(self) -> None: - await self._channel.send("bringToFront", None) + await self._channel.bringToFront() async def hide_highlight(self) -> None: - await self._channel.send("hideHighlight", None) + await self._channel.hideHighlight() async def add_init_script( - self, script: str = None, path: Union[str, Path] = None + self, script: Optional[str], path: Optional[Union[str, Path]] ) -> Disposable: if path: script = add_source_url_to_script( @@ -688,11 +704,11 @@ async def add_init_script( if not isinstance(script, str): raise Error("Either path or script parameter must be specified") return from_channel( - await self._channel.send("addInitScript", None, dict(source=script)) + (await self._channel.addInitScript(source=script))["disposable"] ) async def route( - self, url: URLMatch, handler: RouteHandlerCallback, times: int = None + self, url: URLMatch, handler: RouteHandlerCallback, times: Optional[int] ) -> DisposableStub: self._routes.insert( 0, @@ -705,10 +721,10 @@ async def route( ), ) await self._update_interception_patterns() - return DisposableStub(lambda: self.unroute(url, handler), self) + return DisposableStub(lambda: self.unroute(url, handler=handler), self) async def unroute( - self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None + self, url: URLMatch, handler: Optional[RouteHandlerCallback] ) -> None: removed = [] remaining = [] @@ -750,7 +766,7 @@ def _dispose_har_routers(self) -> None: self._har_routers = [] async def unroute_all( - self, behavior: Literal["default", "ignoreErrors", "wait"] = None + self, behavior: Optional[Literal["default", "ignoreErrors", "wait"]] ) -> None: await self._unroute_internal(self._routes, [], behavior) self._dispose_har_routers() @@ -758,85 +774,82 @@ async def unroute_all( async def route_from_har( self, har: Union[Path, str], - url: Union[Pattern[str], str] = None, - notFound: RouteFromHarNotFoundPolicy = None, - update: bool = None, - updateContent: Literal["attach", "embed"] = None, - updateMode: HarMode = None, + url: Optional[Union[Pattern[str], str]], + not_found: Optional[RouteFromHarNotFoundPolicy], + update: Optional[bool], + update_content: Optional[Literal["attach", "embed"]], + update_mode: Optional[HarMode], ) -> None: if update: await self._browser_context._tracing._record_into_har( har=har, page=self, url=url, - update_content=updateContent, - update_mode=updateMode, + update_content=update_content, + update_mode=update_mode, ) return router = await HarRouter.create( local_utils=self._connection.local_utils, file=str(har), - not_found_action=notFound or "abort", + not_found_action=not_found or "abort", url_matcher=url, ) self._har_routers.append(router) await router.add_page_route(self) async def _update_interception_patterns(self) -> None: - patterns = RouteHandler.prepare_interception_patterns(self._routes) - await self._channel.send( - "setNetworkInterceptionPatterns", - None, - {"patterns": patterns}, + await self._channel.setNetworkInterceptionPatterns( + patterns=RouteHandler.prepare_interception_patterns(self._routes) ) async def _update_web_socket_interception_patterns(self) -> None: - patterns = WebSocketRouteHandler.prepare_interception_patterns( - self._web_socket_routes - ) - await self._channel.send( - "setWebSocketInterceptionPatterns", - None, - {"patterns": patterns}, + await self._channel.setWebSocketInterceptionPatterns( + patterns=WebSocketRouteHandler.prepare_interception_patterns( + self._web_socket_routes + ) ) async def screenshot( self, - timeout: float = None, - type: Literal["jpeg", "png", "webp"] = None, - path: Union[str, Path] = None, - quality: int = None, - omitBackground: bool = None, - fullPage: bool = None, - clip: FloatRect = None, - animations: Literal["allow", "disabled"] = None, - caret: Literal["hide", "initial"] = None, - scale: Literal["css", "device"] = None, - mask: Sequence["Locator"] = None, - maskColor: str = None, - style: str = None, + timeout: Optional[float], + type: Optional[Literal["jpeg", "png", "webp"]], + path: Optional[Union[str, Path]], + quality: Optional[int], + omit_background: Optional[bool], + full_page: Optional[bool], + clip: Optional[FloatRect], + animations: Optional[Literal["allow", "disabled"]], + caret: Optional[Literal["hide", "initial"]], + scale: Optional[Literal["css", "device"]], + mask: Optional[Sequence["Locator"]], + mask_color: Optional[str], + style: Optional[str], ) -> bytes: - params = locals_to_params(locals()) - if "path" in params: - if "type" not in params: - params["type"] = determine_screenshot_type(params["path"]) - del params["path"] - if "mask" in params: - params["mask"] = list( - map( - lambda locator: ( - { - "frame": locator._frame._channel, - "selector": locator._selector, - } - ), - params["mask"], - ) - ) - encoded_binary = await self._channel.send( - "screenshot", self._timeout_settings.timeout, params + if path is not None and type is None: + type = determine_screenshot_type(path) + result = await self._channel.screenshot( + type=type, + quality=quality, + fullPage=full_page, + clip=clip, + omitBackground=omit_background, + caret=caret, + animations=animations, + scale=scale, + mask=( + [ + {"frame": locator._frame._channel, "selector": locator._selector} + for locator in mask + ] + if mask is not None + else None + ), + maskColor=mask_color, + style=style, + timeout=self._timeout_settings.timeout(timeout), ) - decoded_binary = base64.b64decode(encoded_binary) + decoded_binary = base64.b64decode(result["binary"]) if path: make_dirs_for_file(path) await async_writefile(path, decoded_binary) @@ -847,30 +860,35 @@ async def title(self) -> str: async def aria_snapshot( self, - timeout: float = None, - depth: int = None, - mode: Literal["ai", "default"] = None, - boxes: bool = None, + timeout: Optional[float], + depth: Optional[int], + mode: Optional[Literal["ai", "default"]], + boxes: Optional[bool], ) -> str: - return await self._main_frame._channel.send( - "ariaSnapshot", - self._main_frame._timeout, - locals_to_params(locals()), + result = await self._main_frame._channel.ariaSnapshot( + mode=mode, + selector=None, + depth=depth, + boxes=boxes, + timeout=self._main_frame._timeout(timeout), ) + return result["snapshot"] - async def close(self, runBeforeUnload: bool = None, reason: str = None) -> None: + async def close( + self, run_before_unload: Optional[bool], reason: Optional[str] + ) -> None: self._close_reason = reason - if not runBeforeUnload: + if not run_before_unload: self._close_was_called = True try: if self._owned_context: - await self._owned_context.close() - elif runBeforeUnload: - await self._channel.send("runBeforeUnload", None) + await self._owned_context.close(reason=None) + elif run_before_unload: + await self._channel.runBeforeUnload() else: - await self._channel.send("close", None, {"reason": reason}) + await self._channel.close(reason=reason) except Exception as e: - if is_target_closed_error(e) and not runBeforeUnload: + if is_target_closed_error(e) and not run_before_unload: return raise e @@ -880,115 +898,159 @@ def is_closed(self) -> bool: async def click( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - clickCount: int = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - trial: bool = None, - strict: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + click_count: Optional[int], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + trial: Optional[bool], + strict: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - return await self._main_frame._click(**locals_to_params(locals())) + await self._main_frame.click( + selector, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + click_count=click_count, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + trial=trial, + strict=strict, + scroll=scroll, + ) async def dblclick( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - delay: float = None, - button: MouseButton = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + delay: Optional[float], + button: Optional[MouseButton], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - return await self._main_frame.dblclick(**locals_to_params(locals())) + await self._main_frame.dblclick( + selector, + modifiers=modifiers, + position=position, + delay=delay, + button=button, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + strict=strict, + trial=trial, + scroll=scroll, + ) async def tap( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - return await self._main_frame.tap(**locals_to_params(locals())) + await self._main_frame.tap( + selector, + modifiers=modifiers, + position=position, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + strict=strict, + trial=trial, + scroll=scroll, + ) async def fill( self, selector: str, value: str, - timeout: float = None, - noWaitAfter: bool = None, - strict: bool = None, - force: bool = None, + timeout: Optional[float], + no_wait_after: Optional[bool], + strict: Optional[bool], + force: Optional[bool], ) -> None: - return await self._main_frame.fill(**locals_to_params(locals())) + await self._main_frame.fill( + selector, + value, + timeout=timeout, + no_wait_after=no_wait_after, + strict=strict, + force=force, + ) def locator( self, selector: str, - hasText: Union[str, Pattern[str]] = None, - hasNotText: Union[str, Pattern[str]] = None, - has: "Locator" = None, - hasNot: "Locator" = None, + has_text: Optional[Union[str, Pattern[str]]], + has_not_text: Optional[Union[str, Pattern[str]]], + has: Optional["Locator"], + has_not: Optional["Locator"], ) -> "Locator": return self._main_frame.locator( selector, - hasText=hasText, - hasNotText=hasNotText, + has_text=has_text, + has_not_text=has_not_text, has=has, - hasNot=hasNot, + has_not=has_not, ) def get_by_alt_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": return self._main_frame.get_by_alt_text(text, exact=exact) def get_by_label( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": return self._main_frame.get_by_label(text, exact=exact) def get_by_placeholder( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": return self._main_frame.get_by_placeholder(text, exact=exact) def get_by_ref(self, ref: str) -> "Locator": - return self.locator(f"aria-ref={ref}") + return self.locator( + f"aria-ref={ref}", has_text=None, has_not_text=None, has=None, has_not=None + ) def get_by_role( self, role: AriaRole, - checked: bool = None, - disabled: bool = None, - expanded: bool = None, - includeHidden: bool = None, - level: int = None, - name: Union[str, Pattern[str]] = None, - pressed: bool = None, - selected: bool = None, - exact: bool = None, - description: Union[str, Pattern[str]] = None, + checked: Optional[bool], + disabled: Optional[bool], + expanded: Optional[bool], + include_hidden: Optional[bool], + level: Optional[int], + name: Optional[Union[str, Pattern[str]]], + pressed: Optional[bool], + selected: Optional[bool], + exact: Optional[bool], + description: Optional[Union[str, Pattern[str]]], ) -> "Locator": return self._main_frame.get_by_role( role, checked=checked, disabled=disabled, expanded=expanded, - includeHidden=includeHidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -997,97 +1059,156 @@ def get_by_role( description=description, ) - def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator": - return self._main_frame.get_by_test_id(testId) + def get_by_test_id(self, test_id: Union[str, Pattern[str]]) -> "Locator": + return self._main_frame.get_by_test_id(test_id) def get_by_text( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": return self._main_frame.get_by_text(text, exact=exact) def get_by_title( - self, text: Union[str, Pattern[str]], exact: bool = None + self, text: Union[str, Pattern[str]], exact: Optional[bool] ) -> "Locator": return self._main_frame.get_by_title(text, exact=exact) - def frame_locator(self, selector: str = None) -> "FrameLocator": - return self.main_frame.frame_locator(selector) + def frame_locator(self, selector: Optional[str]) -> "FrameLocator": + return self.main_frame.frame_locator(selector=selector) async def focus( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> None: - return await self._main_frame.focus(**locals_to_params(locals())) + await self._main_frame.focus(selector, strict=strict, timeout=timeout) async def text_content( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> Optional[str]: - return await self._main_frame.text_content(**locals_to_params(locals())) + return await self._main_frame.text_content( + selector, strict=strict, timeout=timeout + ) async def inner_text( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> str: - return await self._main_frame.inner_text(**locals_to_params(locals())) + return await self._main_frame.inner_text( + selector, strict=strict, timeout=timeout + ) async def inner_html( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> str: - return await self._main_frame.inner_html(**locals_to_params(locals())) + return await self._main_frame.inner_html( + selector, strict=strict, timeout=timeout + ) async def get_attribute( - self, selector: str, name: str, strict: bool = None, timeout: float = None + self, + selector: str, + name: str, + strict: Optional[bool], + timeout: Optional[float], ) -> Optional[str]: - return await self._main_frame.get_attribute(**locals_to_params(locals())) + return await self._main_frame.get_attribute( + selector, name, strict=strict, timeout=timeout + ) async def hover( self, selector: str, - modifiers: Sequence[KeyboardModifier] = None, - position: Position = None, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + modifiers: Optional[Sequence[KeyboardModifier]], + position: Optional[Position], + timeout: Optional[float], + no_wait_after: Optional[bool], + force: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - return await self._main_frame.hover(**locals_to_params(locals())) + await self._main_frame.hover( + selector, + modifiers=modifiers, + position=position, + timeout=timeout, + no_wait_after=no_wait_after, + force=force, + strict=strict, + trial=trial, + scroll=scroll, + ) async def drag_and_drop( self, source: str, target: str, - sourcePosition: Position = None, - targetPosition: Position = None, - force: bool = None, - noWaitAfter: bool = None, - timeout: float = None, - strict: bool = None, - trial: bool = None, - steps: int = None, - scroll: Literal["auto", "none"] = None, + source_position: Optional[Position], + target_position: Optional[Position], + force: Optional[bool], + no_wait_after: Optional[bool], + timeout: Optional[float], + strict: Optional[bool], + trial: Optional[bool], + steps: Optional[int], + scroll: Optional[Literal["auto", "none"]], ) -> None: - return await self._main_frame.drag_and_drop(**locals_to_params(locals())) + await self._main_frame.drag_and_drop( + source, + target, + source_position=source_position, + target_position=target_position, + force=force, + no_wait_after=no_wait_after, + timeout=timeout, + strict=strict, + trial=trial, + steps=steps, + scroll=scroll, + ) async def select_option( self, selector: str, - value: Union[str, Sequence[str]] = None, - index: Union[int, Sequence[int]] = None, - label: Union[str, Sequence[str]] = None, - element: Union["ElementHandle", Sequence["ElementHandle"]] = None, - timeout: float = None, - noWaitAfter: bool = None, - force: bool = None, - strict: bool = None, + value: Optional[Union[str, Sequence[str]]], + index: Optional[Union[int, Sequence[int]]], + label: Optional[Union[str, Sequence[str]]], + element: Optional[Union["ElementHandle", Sequence["ElementHandle"]]], + timeout: Optional[float], + no_wait_after: Optional[bool], + force: Optional[bool], + strict: Optional[bool], ) -> List[str]: - params = locals_to_params(locals()) - return await self._main_frame.select_option(**params) + return await self._main_frame.select_option( + selector, + value=value, + index=index, + label=label, + element=element, + timeout=timeout, + no_wait_after=no_wait_after, + force=force, + strict=strict, + ) async def input_value( - self, selector: str, strict: bool = None, timeout: float = None + self, + selector: str, + strict: Optional[bool], + timeout: Optional[float], ) -> str: - params = locals_to_params(locals()) - return await self._main_frame.input_value(**params) + return await self._main_frame.input_value( + selector, strict=strict, timeout=timeout + ) async def set_input_files( self, @@ -1095,59 +1216,97 @@ async def set_input_files( files: Union[ str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload] ], - timeout: float = None, - strict: bool = None, - noWaitAfter: bool = None, + timeout: Optional[float], + strict: Optional[bool], + no_wait_after: Optional[bool], ) -> None: - return await self._main_frame.set_input_files(**locals_to_params(locals())) + await self._main_frame.set_input_files( + selector, + files, + timeout=timeout, + strict=strict, + no_wait_after=no_wait_after, + ) async def type( self, selector: str, text: str, - delay: float = None, - timeout: float = None, - noWaitAfter: bool = None, - strict: bool = None, + delay: Optional[float], + timeout: Optional[float], + no_wait_after: Optional[bool], + strict: Optional[bool], ) -> None: - return await self._main_frame.type(**locals_to_params(locals())) + await self._main_frame.type( + selector, + text, + delay=delay, + timeout=timeout, + no_wait_after=no_wait_after, + strict=strict, + ) async def press( self, selector: str, key: str, - delay: float = None, - timeout: float = None, - noWaitAfter: bool = None, - strict: bool = None, + delay: Optional[float], + timeout: Optional[float], + no_wait_after: Optional[bool], + strict: Optional[bool], ) -> None: - return await self._main_frame.press(**locals_to_params(locals())) + await self._main_frame.press( + selector, + key, + delay=delay, + timeout=timeout, + no_wait_after=no_wait_after, + strict=strict, + ) async def check( self, selector: str, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - return await self._main_frame.check(**locals_to_params(locals())) + await self._main_frame.check( + selector, + position=position, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + strict=strict, + trial=trial, + scroll=scroll, + ) async def uncheck( self, selector: str, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: - return await self._main_frame.uncheck(**locals_to_params(locals())) + await self._main_frame.uncheck( + selector, + position=position, + timeout=timeout, + force=force, + no_wait_after=no_wait_after, + strict=strict, + trial=trial, + scroll=scroll, + ) async def wait_for_timeout(self, timeout: float) -> None: await self._main_frame.wait_for_timeout(timeout) @@ -1155,11 +1314,13 @@ async def wait_for_timeout(self, timeout: float) -> None: async def wait_for_function( self, expression: str, - arg: Serializable = None, - timeout: float = None, - polling: Union[float, Literal["raf"]] = None, + arg: Optional[Serializable], + timeout: Optional[float], + polling: Optional[Union[float, Literal["raf"]]], ) -> JSHandle: - return await self._main_frame.wait_for_function(**locals_to_params(locals())) + return await self._main_frame.wait_for_function( + expression, arg=arg, timeout=timeout, polling=polling + ) @property def workers(self) -> List["Worker"]: @@ -1179,9 +1340,7 @@ async def pause(self) -> None: try: await asyncio.wait( [ - asyncio.create_task( - self._browser_context._channel.send("pause", None) - ), + asyncio.create_task(self._browser_context._channel.pause()), self._closed_or_crashed_future, ], return_when=asyncio.FIRST_COMPLETED, @@ -1194,33 +1353,48 @@ async def pause(self) -> None: async def pdf( self, - scale: float = None, - displayHeaderFooter: bool = None, - headerTemplate: str = None, - footerTemplate: str = None, - printBackground: bool = None, - landscape: bool = None, - pageRanges: str = None, - format: str = None, - width: Union[str, float] = None, - height: Union[str, float] = None, - preferCSSPageSize: bool = None, - margin: PdfMargins = None, - path: Union[str, Path] = None, - outline: bool = None, - tagged: bool = None, + scale: Optional[float], + display_header_footer: Optional[bool], + header_template: Optional[str], + footer_template: Optional[str], + print_background: Optional[bool], + landscape: Optional[bool], + page_ranges: Optional[str], + format: Optional[str], + width: Optional[Union[str, float]], + height: Optional[Union[str, float]], + prefer_css_page_size: Optional[bool], + margin: Optional[PdfMargins], + path: Optional[Union[str, Path]], + outline: Optional[bool], + tagged: Optional[bool], ) -> bytes: - params = locals_to_params(locals()) - if "path" in params: - del params["path"] - for key in ["width", "height"]: - if key in params: - params[key] = to_pdf_size(params[key]) - for key in ["top", "right", "bottom", "left"]: - if key in params.get("margin", {}): - params["margin"][key] = to_pdf_size(params["margin"][key]) - encoded_binary = await self._channel.send("pdf", None, params) - decoded_binary = base64.b64decode(encoded_binary) + result = await self._channel.pdf( + scale=scale, + displayHeaderFooter=display_header_footer, + headerTemplate=header_template, + footerTemplate=footer_template, + printBackground=print_background, + landscape=landscape, + pageRanges=page_ranges, + format=format, + width=to_pdf_size(width), + height=to_pdf_size(height), + preferCSSPageSize=prefer_css_page_size, + margin=( + { + "top": to_pdf_size(margin.get("top")), + "right": to_pdf_size(margin.get("right")), + "bottom": to_pdf_size(margin.get("bottom")), + "left": to_pdf_size(margin.get("left")), + } + if margin is not None + else None + ), + tagged=tagged, + outline=outline, + ) + decoded_binary = base64.b64decode(result["pdf"]) if path: make_dirs_for_file(path) await async_writefile(path, decoded_binary) @@ -1258,8 +1432,8 @@ def _close_error_with_reason(self) -> TargetClosedError: def expect_event( self, event: str, - predicate: Callable = None, - timeout: float = None, + predicate: Optional[Callable], + timeout: Optional[float], ) -> EventContextManagerImpl: return self._expect_event( event, predicate, timeout, f'waiting for event "{event}"' @@ -1291,55 +1465,65 @@ def _expect_event( def expect_console_message( self, - predicate: Callable[[ConsoleMessage], bool] = None, - timeout: float = None, + predicate: Optional[Callable[[ConsoleMessage], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl[ConsoleMessage]: - return self.expect_event(Page.Events.Console, predicate, timeout) + return self.expect_event( + Page.Events.Console, predicate=predicate, timeout=timeout + ) def expect_download( self, - predicate: Callable[[Download], bool] = None, - timeout: float = None, + predicate: Optional[Callable[[Download], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl[Download]: - return self.expect_event(Page.Events.Download, predicate, timeout) + return self.expect_event( + Page.Events.Download, predicate=predicate, timeout=timeout + ) def expect_file_chooser( self, - predicate: Callable[[FileChooser], bool] = None, - timeout: float = None, + predicate: Optional[Callable[[FileChooser], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl[FileChooser]: - return self.expect_event(Page.Events.FileChooser, predicate, timeout) + return self.expect_event( + Page.Events.FileChooser, predicate=predicate, timeout=timeout + ) def expect_navigation( self, - url: URLMatch = None, - waitUntil: DocumentLoadState = None, - timeout: float = None, + url: Optional[URLMatch], + wait_until: Optional[DocumentLoadState], + timeout: Optional[float], ) -> EventContextManagerImpl[Response]: - return self.main_frame.expect_navigation(url, waitUntil, timeout) + return self.main_frame.expect_navigation( + url=url, wait_until=wait_until, timeout=timeout + ) def expect_popup( self, - predicate: Callable[["Page"], bool] = None, - timeout: float = None, + predicate: Optional[Callable[["Page"], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl["Page"]: - return self.expect_event(Page.Events.Popup, predicate, timeout) + return self.expect_event( + Page.Events.Popup, predicate=predicate, timeout=timeout + ) def expect_request( self, - urlOrPredicate: URLMatchRequest, - timeout: float = None, + url_or_predicate: URLMatchRequest, + timeout: Optional[float], ) -> EventContextManagerImpl[Request]: def my_predicate(request: Request) -> Union[bool, Awaitable[bool]]: - if not callable(urlOrPredicate): + if not callable(url_or_predicate): return url_matches( self._browser_context._base_url, request.url, - urlOrPredicate, + url_or_predicate, ) - return urlOrPredicate(request) + return url_or_predicate(request) - trimmed_url = trim_url(urlOrPredicate) + trimmed_url = trim_url(url_or_predicate) log_line = f"waiting for request {trimmed_url}" if trimmed_url else None return self._expect_event( Page.Events.Request, @@ -1350,8 +1534,8 @@ def my_predicate(request: Request) -> Union[bool, Awaitable[bool]]: def expect_request_finished( self, - predicate: Callable[["Request"], bool] = None, - timeout: float = None, + predicate: Optional[Callable[["Request"], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl[Request]: return self.expect_event( Page.Events.RequestFinished, predicate=predicate, timeout=timeout @@ -1359,19 +1543,19 @@ def expect_request_finished( def expect_response( self, - urlOrPredicate: URLMatchResponse, - timeout: float = None, + url_or_predicate: URLMatchResponse, + timeout: Optional[float], ) -> EventContextManagerImpl[Response]: def my_predicate(request: Response) -> Union[bool, Awaitable[bool]]: - if not callable(urlOrPredicate): + if not callable(url_or_predicate): return url_matches( self._browser_context._base_url, request.url, - urlOrPredicate, + url_or_predicate, ) - return urlOrPredicate(request) + return url_or_predicate(request) - trimmed_url = trim_url(urlOrPredicate) + trimmed_url = trim_url(url_or_predicate) log_line = f"waiting for response {trimmed_url}" if trimmed_url else None return self._expect_event( Page.Events.Response, @@ -1382,46 +1566,48 @@ def my_predicate(request: Response) -> Union[bool, Awaitable[bool]]: def expect_websocket( self, - predicate: Callable[["WebSocket"], bool] = None, - timeout: float = None, + predicate: Optional[Callable[["WebSocket"], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl["WebSocket"]: - return self.expect_event("websocket", predicate, timeout) + return self.expect_event("websocket", predicate=predicate, timeout=timeout) def expect_worker( self, - predicate: Callable[["Worker"], bool] = None, - timeout: float = None, + predicate: Optional[Callable[["Worker"], bool]], + timeout: Optional[float], ) -> EventContextManagerImpl["Worker"]: - return self.expect_event("worker", predicate, timeout) + return self.expect_event("worker", predicate=predicate, timeout=timeout) async def set_checked( self, selector: str, checked: bool, - position: Position = None, - timeout: float = None, - force: bool = None, - noWaitAfter: bool = None, - strict: bool = None, - trial: bool = None, - scroll: Literal["auto", "none"] = None, + position: Optional[Position], + timeout: Optional[float], + force: Optional[bool], + no_wait_after: Optional[bool], + strict: Optional[bool], + trial: Optional[bool], + scroll: Optional[Literal["auto", "none"]], ) -> None: if checked: await self.check( - selector=selector, + selector, position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, strict=strict, trial=trial, scroll=scroll, ) else: await self.uncheck( - selector=selector, + selector, position=position, timeout=timeout, force=force, + no_wait_after=no_wait_after, strict=strict, trial=trial, scroll=scroll, @@ -1431,26 +1617,22 @@ async def add_locator_handler( self, locator: "Locator", handler: Union[Callable[["Locator"], Any], Callable[[], Any]], - noWaitAfter: bool = None, - times: int = None, + no_wait_after: Optional[bool], + times: Optional[int], ) -> 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", - None, - { - "selector": locator._selector, - "noWaitAfter": noWaitAfter, - }, + result = await self._channel.registerLocatorHandler( + selector=locator._selector, noWaitAfter=no_wait_after ) + uid = result["uid"] self._locator_handlers[uid] = LocatorHandler( handler=handler, times=times, locator=locator ) - async def _on_locator_handler_triggered(self, uid: str) -> None: + async def _on_locator_handler_triggered(self, uid: int) -> None: remove = False try: handler = self._locator_handlers.get(uid) @@ -1480,10 +1662,8 @@ def _handler() -> None: del self._locator_handlers[uid] try: await self._connection.wrap_api_call( - lambda: self._channel.send( - "resolveLocatorHandlerNoReply", - None, - {"uid": uid, "remove": remove}, + lambda: self._channel.resolveLocatorHandlerNoReply( + uid=uid, remove=remove ), is_internal=True, ) @@ -1494,52 +1674,50 @@ 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", - None, - {"uid": uid}, - ) + self._channel.send_no_reply("unregisterLocatorHandler", {"uid": uid}) async def requests(self) -> List[Request]: - request_objects = await self._channel.send("requests", None) - return [from_channel(r) for r in request_objects] + result = await self._channel.requests() + return [from_channel(r) for r in result["requests"]] async def console_messages( - self, filter: Literal["all", "since-navigation"] = None + self, filter: Optional[Literal["all", "since-navigation"]] ) -> List[ConsoleMessage]: - message_dicts = await self._channel.send( - "consoleMessages", None, locals_to_params(locals()) - ) + result = await self._channel.consoleMessages(filter=filter) return [ ConsoleMessage( - {**event, "page": self._channel}, self._loop, self._dispatcher_fiber + {**event, "page": self._channel, "worker": None}, + self._loop, + self._dispatcher_fiber, ) - for event in message_dicts + for event in result["messages"] ] async def page_errors( - self, filter: Literal["all", "since-navigation"] = None + self, filter: Optional[Literal["all", "since-navigation"]] ) -> List[Error]: - error_objects = await self._channel.send( - "pageErrors", None, locals_to_params(locals()) - ) - return [parse_error(error["error"]) for error in error_objects] + result = await self._channel.pageErrors(filter=filter) + return [parse_error(error["error"]) for error in result["errors"]] async def clear_console_messages(self) -> None: - await self._channel.send("clearConsoleMessages", None) + await self._channel.clearConsoleMessages() async def clear_page_errors(self) -> None: - await self._channel.send("clearPageErrors", None) + await self._channel.clearPageErrors() async def pick_locator(self) -> "Locator": - selector = await self._channel.send("pickLocator", None, {}) - return self.locator(selector) + selector = (await self._channel.pickLocator())["selector"] + return self.locator( + selector, has_text=None, has_not_text=None, has=None, has_not=None + ) async def cancel_pick_locator(self) -> None: - await self._channel.send("cancelPickLocator", None, {}) + await self._channel.cancelPickLocator() class Worker(ChannelOwner): + _channel: "WorkerChannel" + Events = SimpleNamespace(Close="close", Console="console") def __init__( @@ -1547,7 +1725,7 @@ def __init__( ) -> None: super().__init__(parent, type, guid, initializer) self._set_event_to_subscription_mapping({Worker.Events.Console: "console"}) - self._channel.on("close", lambda _: self._on_close()) + self._channel.onClose(lambda: self._on_close()) self._page: Optional[Page] = None self._context: Optional["BrowserContext"] = None @@ -1565,37 +1743,25 @@ def _on_close(self) -> None: def url(self) -> str: return self._initializer["url"] - async def evaluate(self, expression: str, arg: Serializable = None) -> Any: - return parse_result( - await self._channel.send( - "evaluateExpression", - None, - dict( - expression=expression, - arg=serialize_argument(arg), - ), - ) + async def evaluate(self, expression: str, arg: Optional[Serializable]) -> Any: + result = await self._channel.evaluateExpression( + expression=expression, isFunction=None, arg=serialize_argument(arg) ) + return parse_result(result["value"]) async def evaluate_handle( - self, expression: str, arg: Serializable = None + self, expression: str, arg: Optional[Serializable] ) -> JSHandle: - return from_channel( - await self._channel.send( - "evaluateExpressionHandle", - None, - dict( - expression=expression, - arg=serialize_argument(arg), - ), - ) + result = await self._channel.evaluateExpressionHandle( + expression=expression, isFunction=None, arg=serialize_argument(arg) ) + return from_channel(result["handle"]) def expect_event( self, event: str, - predicate: Callable = None, - timeout: float = None, + predicate: Optional[Callable], + timeout: Optional[float], ) -> EventContextManagerImpl: if timeout is None: if self._page: @@ -1618,6 +1784,8 @@ def expect_event( class BindingCall(ChannelOwner): + _channel: "BindingCallChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -1634,13 +1802,15 @@ async def call(self, func: Callable) -> None: result = func(source, *func_args) if inspect.iscoroutine(result): result = await result - self._channel.send_may_fail( - "resolve", None, dict(result=serialize_argument(result)) + create_task_and_ignore_exception( + self._loop, + self._channel.resolve(result=serialize_argument(result)), ) except Exception as e: tb = sys.exc_info()[2] - self._channel.send_may_fail( - "reject", None, dict(error=dict(error=serialize_error(e, tb))) + create_task_and_ignore_exception( + self._loop, + self._channel.reject(error={"error": serialize_error(e, tb)}), ) diff --git a/playwright/_impl/_playwright.py b/playwright/_impl/_playwright.py index 5c0151158..0210b8b66 100644 --- a/playwright/_impl/_playwright.py +++ b/playwright/_impl/_playwright.py @@ -12,15 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict +from typing import TYPE_CHECKING, Dict from playwright._impl._browser_type import BrowserType from playwright._impl._connection import ChannelOwner, from_channel from playwright._impl._fetch import APIRequest from playwright._impl._selectors import Selectors +if TYPE_CHECKING: # pragma: no cover + from playwright._impl._protocol_channels import PlaywrightChannel + class Playwright(ChannelOwner): + _channel: "PlaywrightChannel" + devices: Dict selectors: Selectors chromium: BrowserType diff --git a/playwright/_impl/_protocol_channels.py b/playwright/_impl/_protocol_channels.py new file mode 100644 index 000000000..4c459dd8e --- /dev/null +++ b/playwright/_impl/_protocol_channels.py @@ -0,0 +1,5160 @@ +# Copyright (c) Microsoft Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Typed channels for the protocol interfaces, generated from packages/protocol/spec. + +Like upstream's channels in packages/protocol/src/channels.d.ts: each command is +an async method with the protocol name and parameters, and each event has +on and off methods. Every parameter must be passed; an absent +optional parameter is None. The result and the event payloads have every +top-level field; a field that the driver left out is None. `timeout` is the +timeout of the message, in milliseconds; 0 means no timeout. + +Generated by scripts/codegen/generate_protocol.py. Do not edit. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable, Dict, Literal, Optional, Sequence, Type + +from playwright._impl._connection import Channel + +if TYPE_CHECKING: + from playwright._impl import _protocol_types as channels + + +class AndroidChannel(Channel): + async def devices( + self, + host: Optional[str], + port: Optional[int], + omitDriverInstall: Optional[bool], + timeout: float = 0, + ) -> channels.AndroidDevicesResult: + result = await self._call( + "devices", + {"host": host, "port": port, "omitDriverInstall": omitDriverInstall}, + timeout, + ) + return {"devices": result["devices"]} + + +class AndroidSocketChannel(Channel): + async def write( + self, + data: str, + timeout: float = 0, + ) -> None: + await self._call("write", {"data": data}, timeout) + + async def close( + self, + timeout: float = 0, + ) -> None: + await self._call("close", {}, timeout) + + def onData(self, handler: Callable[[channels.AndroidSocketDataEvent], Any]) -> None: + self._add_event_listener( + "data", handler, lambda event: handler({"data": event["data"]}) + ) + + def offData( + self, handler: Callable[[channels.AndroidSocketDataEvent], Any] + ) -> None: + self._remove_event_listener("data", handler) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + +class AndroidDeviceChannel(Channel): + async def wait( + self, + androidSelector: channels.AndroidSelector, + state: Optional[Literal["gone"]], + timeout: float = 0, + ) -> None: + await self._call( + "wait", {"androidSelector": androidSelector, "state": state}, timeout + ) + + async def fill( + self, + androidSelector: channels.AndroidSelector, + text: str, + timeout: float = 0, + ) -> None: + await self._call( + "fill", {"androidSelector": androidSelector, "text": text}, timeout + ) + + async def tap( + self, + androidSelector: channels.AndroidSelector, + duration: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "tap", {"androidSelector": androidSelector, "duration": duration}, timeout + ) + + async def drag( + self, + androidSelector: channels.AndroidSelector, + dest: channels.Point, + speed: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "drag", + {"androidSelector": androidSelector, "dest": dest, "speed": speed}, + timeout, + ) + + async def fling( + self, + androidSelector: channels.AndroidSelector, + direction: Literal["up", "down", "left", "right"], + speed: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "fling", + { + "androidSelector": androidSelector, + "direction": direction, + "speed": speed, + }, + timeout, + ) + + async def longTap( + self, + androidSelector: channels.AndroidSelector, + timeout: float = 0, + ) -> None: + await self._call("longTap", {"androidSelector": androidSelector}, timeout) + + async def pinchClose( + self, + androidSelector: channels.AndroidSelector, + percent: float, + speed: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "pinchClose", + {"androidSelector": androidSelector, "percent": percent, "speed": speed}, + timeout, + ) + + async def pinchOpen( + self, + androidSelector: channels.AndroidSelector, + percent: float, + speed: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "pinchOpen", + {"androidSelector": androidSelector, "percent": percent, "speed": speed}, + timeout, + ) + + async def scroll( + self, + androidSelector: channels.AndroidSelector, + direction: Literal["up", "down", "left", "right"], + percent: float, + speed: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "scroll", + { + "androidSelector": androidSelector, + "direction": direction, + "percent": percent, + "speed": speed, + }, + timeout, + ) + + async def swipe( + self, + androidSelector: channels.AndroidSelector, + direction: Literal["up", "down", "left", "right"], + percent: float, + speed: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "swipe", + { + "androidSelector": androidSelector, + "direction": direction, + "percent": percent, + "speed": speed, + }, + timeout, + ) + + async def info( + self, + androidSelector: channels.AndroidSelector, + timeout: float = 0, + ) -> channels.AndroidDeviceInfoResult: + result = await self._call("info", {"androidSelector": androidSelector}, timeout) + return {"info": result["info"]} + + async def screenshot( + self, + timeout: float = 0, + ) -> channels.AndroidDeviceScreenshotResult: + result = await self._call("screenshot", {}, timeout) + return {"binary": result["binary"]} + + async def inputType( + self, + text: str, + timeout: float = 0, + ) -> None: + await self._call("inputType", {"text": text}, timeout) + + async def inputPress( + self, + key: str, + timeout: float = 0, + ) -> None: + await self._call("inputPress", {"key": key}, timeout) + + async def inputTap( + self, + point: channels.Point, + timeout: float = 0, + ) -> None: + await self._call("inputTap", {"point": point}, timeout) + + async def inputSwipe( + self, + segments: Sequence[channels.Point], + steps: int, + timeout: float = 0, + ) -> None: + await self._call("inputSwipe", {"segments": segments, "steps": steps}, timeout) + + async def inputDrag( + self, + from_: channels.Point, + to: channels.Point, + steps: int, + timeout: float = 0, + ) -> None: + await self._call( + "inputDrag", {"from": from_, "to": to, "steps": steps}, timeout + ) + + async def launchBrowser( + self, + noDefaultViewport: Optional[bool], + viewport: Optional[channels.AndroidDeviceLaunchBrowserParamsViewport], + screen: Optional[channels.AndroidDeviceLaunchBrowserParamsScreen], + ignoreHTTPSErrors: Optional[bool], + clientCertificates: Optional[ + Sequence[channels.AndroidDeviceLaunchBrowserParamsClientCertificates] + ], + javaScriptEnabled: Optional[bool], + bypassCSP: Optional[bool], + userAgent: Optional[str], + locale: Optional[str], + timezoneId: Optional[str], + geolocation: Optional[channels.AndroidDeviceLaunchBrowserParamsGeolocation], + permissions: Optional[Sequence[str]], + extraHTTPHeaders: Optional[Sequence[channels.NameValue]], + offline: Optional[bool], + httpCredentials: Optional[Sequence[channels.HttpCredentials]], + deviceScaleFactor: Optional[float], + isMobile: Optional[bool], + hasTouch: Optional[bool], + colorScheme: Optional[Literal["dark", "light", "no-preference", "no-override"]], + reducedMotion: Optional[Literal["reduce", "no-preference", "no-override"]], + forcedColors: Optional[Literal["active", "none", "no-override"]], + acceptDownloads: Optional[ + Literal["accept", "deny", "internal-browser-default"] + ], + contrast: Optional[Literal["no-preference", "more", "no-override"]], + baseURL: Optional[str], + recordVideo: Optional[channels.AndroidDeviceLaunchBrowserParamsRecordVideo], + strictSelectors: Optional[bool], + serviceWorkers: Optional[Literal["allow", "block"]], + selectorEngines: Optional[Sequence[channels.SelectorEngine]], + testIdAttributeName: Optional[str], + pkg: Optional[str], + args: Optional[Sequence[str]], + proxy: Optional[channels.AndroidDeviceLaunchBrowserParamsProxy], + timeout: float = 0, + ) -> channels.AndroidDeviceLaunchBrowserResult: + result = await self._call( + "launchBrowser", + { + "noDefaultViewport": noDefaultViewport, + "viewport": viewport, + "screen": screen, + "ignoreHTTPSErrors": ignoreHTTPSErrors, + "clientCertificates": clientCertificates, + "javaScriptEnabled": javaScriptEnabled, + "bypassCSP": bypassCSP, + "userAgent": userAgent, + "locale": locale, + "timezoneId": timezoneId, + "geolocation": geolocation, + "permissions": permissions, + "extraHTTPHeaders": extraHTTPHeaders, + "offline": offline, + "httpCredentials": httpCredentials, + "deviceScaleFactor": deviceScaleFactor, + "isMobile": isMobile, + "hasTouch": hasTouch, + "colorScheme": colorScheme, + "reducedMotion": reducedMotion, + "forcedColors": forcedColors, + "acceptDownloads": acceptDownloads, + "contrast": contrast, + "baseURL": baseURL, + "recordVideo": recordVideo, + "strictSelectors": strictSelectors, + "serviceWorkers": serviceWorkers, + "selectorEngines": selectorEngines, + "testIdAttributeName": testIdAttributeName, + "pkg": pkg, + "args": args, + "proxy": proxy, + }, + timeout, + ) + return {"context": result["context"]} + + async def open( + self, + command: str, + timeout: float = 0, + ) -> channels.AndroidDeviceOpenResult: + result = await self._call("open", {"command": command}, timeout) + return {"socket": result["socket"]} + + async def shell( + self, + command: str, + timeout: float = 0, + ) -> channels.AndroidDeviceShellResult: + result = await self._call("shell", {"command": command}, timeout) + return {"result": result["result"]} + + async def installApk( + self, + file: str, + args: Optional[Sequence[str]], + timeout: float = 0, + ) -> None: + await self._call("installApk", {"file": file, "args": args}, timeout) + + async def push( + self, + file: str, + path: str, + mode: Optional[int], + timeout: float = 0, + ) -> None: + await self._call("push", {"file": file, "path": path, "mode": mode}, timeout) + + async def connectToWebView( + self, + socketName: str, + timeout: float = 0, + ) -> channels.AndroidDeviceConnectToWebViewResult: + result = await self._call( + "connectToWebView", {"socketName": socketName}, timeout + ) + return {"context": result["context"]} + + async def close( + self, + timeout: float = 0, + ) -> None: + await self._call("close", {}, timeout) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + def onWebViewAdded( + self, handler: Callable[[channels.AndroidDeviceWebViewAddedEvent], Any] + ) -> None: + self._add_event_listener( + "webViewAdded", + handler, + lambda event: handler({"webView": event["webView"]}), + ) + + def offWebViewAdded( + self, handler: Callable[[channels.AndroidDeviceWebViewAddedEvent], Any] + ) -> None: + self._remove_event_listener("webViewAdded", handler) + + def onWebViewRemoved( + self, handler: Callable[[channels.AndroidDeviceWebViewRemovedEvent], Any] + ) -> None: + self._add_event_listener( + "webViewRemoved", + handler, + lambda event: handler({"socketName": event["socketName"]}), + ) + + def offWebViewRemoved( + self, handler: Callable[[channels.AndroidDeviceWebViewRemovedEvent], Any] + ) -> None: + self._remove_event_listener("webViewRemoved", handler) + + +class APIRequestContextChannel(Channel): + async def fetch( + self, + url: str, + encodedParams: Optional[str], + params: Optional[Sequence[channels.NameValue]], + method: Optional[str], + headers: Optional[Sequence[channels.NameValue]], + postData: Optional[str], + jsonData: Optional[str], + formData: Optional[Sequence[channels.NameValue]], + multipartData: Optional[Sequence[channels.FormField]], + failOnStatusCode: Optional[bool], + ignoreHTTPSErrors: Optional[bool], + maxRedirects: Optional[int], + maxRetries: Optional[int], + timeout: float = 0, + ) -> channels.APIRequestContextFetchResult: + result = await self._call( + "fetch", + { + "url": url, + "encodedParams": encodedParams, + "params": params, + "method": method, + "headers": headers, + "postData": postData, + "jsonData": jsonData, + "formData": formData, + "multipartData": multipartData, + "failOnStatusCode": failOnStatusCode, + "ignoreHTTPSErrors": ignoreHTTPSErrors, + "maxRedirects": maxRedirects, + "maxRetries": maxRetries, + }, + timeout, + ) + return {"response": result["response"]} + + async def fetchResponseBody( + self, + fetchUid: str, + timeout: float = 0, + ) -> channels.APIRequestContextFetchResponseBodyResult: + result = await self._call("fetchResponseBody", {"fetchUid": fetchUid}, timeout) + return {"binary": result.get("binary")} + + async def fetchLog( + self, + fetchUid: str, + timeout: float = 0, + ) -> channels.APIRequestContextFetchLogResult: + result = await self._call("fetchLog", {"fetchUid": fetchUid}, timeout) + return {"log": result["log"]} + + async def addCookies( + self, + cookies: Sequence[channels.SetNetworkCookie], + timeout: float = 0, + ) -> None: + await self._call("addCookies", {"cookies": cookies}, timeout) + + async def clearCookies( + self, + name: Optional[str], + nameRegexSource: Optional[str], + nameRegexFlags: Optional[str], + domain: Optional[str], + domainRegexSource: Optional[str], + domainRegexFlags: Optional[str], + path: Optional[str], + pathRegexSource: Optional[str], + pathRegexFlags: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clearCookies", + { + "name": name, + "nameRegexSource": nameRegexSource, + "nameRegexFlags": nameRegexFlags, + "domain": domain, + "domainRegexSource": domainRegexSource, + "domainRegexFlags": domainRegexFlags, + "path": path, + "pathRegexSource": pathRegexSource, + "pathRegexFlags": pathRegexFlags, + }, + timeout, + ) + + async def cookies( + self, + urls: Sequence[str], + timeout: float = 0, + ) -> channels.APIRequestContextCookiesResult: + result = await self._call("cookies", {"urls": urls}, timeout) + return {"cookies": result["cookies"]} + + async def storageState( + self, + indexedDB: Optional[bool], + opfs: Optional[bool], + timeout: float = 0, + ) -> channels.APIRequestContextStorageStateResult: + result = await self._call( + "storageState", {"indexedDB": indexedDB, "opfs": opfs}, timeout + ) + return {"cookies": result["cookies"], "origins": result["origins"]} + + async def disposeAPIResponse( + self, + fetchUid: str, + timeout: float = 0, + ) -> None: + await self._call("disposeAPIResponse", {"fetchUid": fetchUid}, timeout) + + async def dispose( + self, + reason: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("dispose", {"reason": reason}, timeout) + + +class ArtifactChannel(Channel): + async def pathAfterFinished( + self, + timeout: float = 0, + ) -> channels.ArtifactPathAfterFinishedResult: + result = await self._call("pathAfterFinished", {}, timeout) + return {"value": result["value"]} + + async def saveAs( + self, + path: str, + timeout: float = 0, + ) -> None: + await self._call("saveAs", {"path": path}, timeout) + + async def saveAsStream( + self, + timeout: float = 0, + ) -> channels.ArtifactSaveAsStreamResult: + result = await self._call("saveAsStream", {}, timeout) + return {"stream": result["stream"]} + + async def failure( + self, + timeout: float = 0, + ) -> channels.ArtifactFailureResult: + result = await self._call("failure", {}, timeout) + return {"error": result.get("error")} + + async def stream( + self, + timeout: float = 0, + ) -> channels.ArtifactStreamResult: + result = await self._call("stream", {}, timeout) + return {"stream": result["stream"]} + + async def cancel( + self, + timeout: float = 0, + ) -> None: + await self._call("cancel", {}, timeout) + + async def delete( + self, + timeout: float = 0, + ) -> None: + await self._call("delete", {}, timeout) + + +class StreamChannel(Channel): + async def read( + self, + size: Optional[int], + timeout: float = 0, + ) -> channels.StreamReadResult: + result = await self._call("read", {"size": size}, timeout) + return {"binary": result["binary"]} + + async def close( + self, + timeout: float = 0, + ) -> None: + await self._call("close", {}, timeout) + + +class WritableStreamChannel(Channel): + async def write( + self, + binary: str, + timeout: float = 0, + ) -> None: + await self._call("write", {"binary": binary}, timeout) + + async def close( + self, + timeout: float = 0, + ) -> None: + await self._call("close", {}, timeout) + + +class BrowserChannel(Channel): + async def startServer( + self, + title: str, + workspaceDir: Optional[str], + metadata: Optional[Any], + host: Optional[str], + port: Optional[int], + timeout: float = 0, + ) -> channels.BrowserStartServerResult: + result = await self._call( + "startServer", + { + "title": title, + "workspaceDir": workspaceDir, + "metadata": metadata, + "host": host, + "port": port, + }, + timeout, + ) + return {"endpoint": result["endpoint"]} + + async def stopServer( + self, + timeout: float = 0, + ) -> None: + await self._call("stopServer", {}, timeout) + + async def close( + self, + reason: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("close", {"reason": reason}, timeout) + + async def killForTests( + self, + timeout: float = 0, + ) -> None: + await self._call("killForTests", {}, timeout) + + async def defaultUserAgentForTest( + self, + timeout: float = 0, + ) -> channels.BrowserDefaultUserAgentForTestResult: + result = await self._call("defaultUserAgentForTest", {}, timeout) + return {"userAgent": result["userAgent"]} + + async def newContext( + self, + noDefaultViewport: Optional[bool], + viewport: Optional[channels.BrowserNewContextParamsViewport], + screen: Optional[channels.BrowserNewContextParamsScreen], + ignoreHTTPSErrors: Optional[bool], + clientCertificates: Optional[ + Sequence[channels.BrowserNewContextParamsClientCertificates] + ], + javaScriptEnabled: Optional[bool], + bypassCSP: Optional[bool], + userAgent: Optional[str], + locale: Optional[str], + timezoneId: Optional[str], + geolocation: Optional[channels.BrowserNewContextParamsGeolocation], + permissions: Optional[Sequence[str]], + extraHTTPHeaders: Optional[Sequence[channels.NameValue]], + offline: Optional[bool], + httpCredentials: Optional[Sequence[channels.HttpCredentials]], + deviceScaleFactor: Optional[float], + isMobile: Optional[bool], + hasTouch: Optional[bool], + colorScheme: Optional[Literal["dark", "light", "no-preference", "no-override"]], + reducedMotion: Optional[Literal["reduce", "no-preference", "no-override"]], + forcedColors: Optional[Literal["active", "none", "no-override"]], + acceptDownloads: Optional[ + Literal["accept", "deny", "internal-browser-default"] + ], + contrast: Optional[Literal["no-preference", "more", "no-override"]], + baseURL: Optional[str], + recordVideo: Optional[channels.BrowserNewContextParamsRecordVideo], + strictSelectors: Optional[bool], + serviceWorkers: Optional[Literal["allow", "block"]], + selectorEngines: Optional[Sequence[channels.SelectorEngine]], + testIdAttributeName: Optional[str], + proxy: Optional[channels.BrowserNewContextParamsProxy], + storageState: Optional[channels.BrowserNewContextParamsStorageState], + timeout: float = 0, + ) -> channels.BrowserNewContextResult: + result = await self._call( + "newContext", + { + "noDefaultViewport": noDefaultViewport, + "viewport": viewport, + "screen": screen, + "ignoreHTTPSErrors": ignoreHTTPSErrors, + "clientCertificates": clientCertificates, + "javaScriptEnabled": javaScriptEnabled, + "bypassCSP": bypassCSP, + "userAgent": userAgent, + "locale": locale, + "timezoneId": timezoneId, + "geolocation": geolocation, + "permissions": permissions, + "extraHTTPHeaders": extraHTTPHeaders, + "offline": offline, + "httpCredentials": httpCredentials, + "deviceScaleFactor": deviceScaleFactor, + "isMobile": isMobile, + "hasTouch": hasTouch, + "colorScheme": colorScheme, + "reducedMotion": reducedMotion, + "forcedColors": forcedColors, + "acceptDownloads": acceptDownloads, + "contrast": contrast, + "baseURL": baseURL, + "recordVideo": recordVideo, + "strictSelectors": strictSelectors, + "serviceWorkers": serviceWorkers, + "selectorEngines": selectorEngines, + "testIdAttributeName": testIdAttributeName, + "proxy": proxy, + "storageState": storageState, + }, + timeout, + ) + return {"context": result["context"]} + + async def newContextForReuse( + self, + noDefaultViewport: Optional[bool], + viewport: Optional[channels.BrowserNewContextForReuseParamsViewport], + screen: Optional[channels.BrowserNewContextForReuseParamsScreen], + ignoreHTTPSErrors: Optional[bool], + clientCertificates: Optional[ + Sequence[channels.BrowserNewContextForReuseParamsClientCertificates] + ], + javaScriptEnabled: Optional[bool], + bypassCSP: Optional[bool], + userAgent: Optional[str], + locale: Optional[str], + timezoneId: Optional[str], + geolocation: Optional[channels.BrowserNewContextForReuseParamsGeolocation], + permissions: Optional[Sequence[str]], + extraHTTPHeaders: Optional[Sequence[channels.NameValue]], + offline: Optional[bool], + httpCredentials: Optional[Sequence[channels.HttpCredentials]], + deviceScaleFactor: Optional[float], + isMobile: Optional[bool], + hasTouch: Optional[bool], + colorScheme: Optional[Literal["dark", "light", "no-preference", "no-override"]], + reducedMotion: Optional[Literal["reduce", "no-preference", "no-override"]], + forcedColors: Optional[Literal["active", "none", "no-override"]], + acceptDownloads: Optional[ + Literal["accept", "deny", "internal-browser-default"] + ], + contrast: Optional[Literal["no-preference", "more", "no-override"]], + baseURL: Optional[str], + recordVideo: Optional[channels.BrowserNewContextForReuseParamsRecordVideo], + strictSelectors: Optional[bool], + serviceWorkers: Optional[Literal["allow", "block"]], + selectorEngines: Optional[Sequence[channels.SelectorEngine]], + testIdAttributeName: Optional[str], + proxy: Optional[channels.BrowserNewContextForReuseParamsProxy], + storageState: Optional[channels.BrowserNewContextForReuseParamsStorageState], + timeout: float = 0, + ) -> channels.BrowserNewContextForReuseResult: + result = await self._call( + "newContextForReuse", + { + "noDefaultViewport": noDefaultViewport, + "viewport": viewport, + "screen": screen, + "ignoreHTTPSErrors": ignoreHTTPSErrors, + "clientCertificates": clientCertificates, + "javaScriptEnabled": javaScriptEnabled, + "bypassCSP": bypassCSP, + "userAgent": userAgent, + "locale": locale, + "timezoneId": timezoneId, + "geolocation": geolocation, + "permissions": permissions, + "extraHTTPHeaders": extraHTTPHeaders, + "offline": offline, + "httpCredentials": httpCredentials, + "deviceScaleFactor": deviceScaleFactor, + "isMobile": isMobile, + "hasTouch": hasTouch, + "colorScheme": colorScheme, + "reducedMotion": reducedMotion, + "forcedColors": forcedColors, + "acceptDownloads": acceptDownloads, + "contrast": contrast, + "baseURL": baseURL, + "recordVideo": recordVideo, + "strictSelectors": strictSelectors, + "serviceWorkers": serviceWorkers, + "selectorEngines": selectorEngines, + "testIdAttributeName": testIdAttributeName, + "proxy": proxy, + "storageState": storageState, + }, + timeout, + ) + return {"context": result["context"]} + + async def disconnectFromReusedContext( + self, + reason: str, + timeout: float = 0, + ) -> None: + await self._call("disconnectFromReusedContext", {"reason": reason}, timeout) + + async def newBrowserCDPSession( + self, + timeout: float = 0, + ) -> channels.BrowserNewBrowserCDPSessionResult: + result = await self._call("newBrowserCDPSession", {}, timeout) + return {"session": result["session"]} + + async def startTracing( + self, + page: Optional[PageChannel], + screenshots: Optional[bool], + categories: Optional[Sequence[str]], + timeout: float = 0, + ) -> None: + await self._call( + "startTracing", + {"page": page, "screenshots": screenshots, "categories": categories}, + timeout, + ) + + async def stopTracing( + self, + timeout: float = 0, + ) -> channels.BrowserStopTracingResult: + result = await self._call("stopTracing", {}, timeout) + return {"artifact": result["artifact"]} + + def onContext(self, handler: Callable[[channels.BrowserContextEvent], Any]) -> None: + self._add_event_listener( + "context", handler, lambda event: handler({"context": event["context"]}) + ) + + def offContext( + self, handler: Callable[[channels.BrowserContextEvent], Any] + ) -> None: + self._remove_event_listener("context", handler) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + +class BrowserContextChannel(Channel): + async def addCookies( + self, + cookies: Sequence[channels.SetNetworkCookie], + timeout: float = 0, + ) -> None: + await self._call("addCookies", {"cookies": cookies}, timeout) + + async def addInitScript( + self, + source: str, + timeout: float = 0, + ) -> channels.BrowserContextAddInitScriptResult: + result = await self._call("addInitScript", {"source": source}, timeout) + return {"disposable": result["disposable"]} + + async def clearCookies( + self, + name: Optional[str], + nameRegexSource: Optional[str], + nameRegexFlags: Optional[str], + domain: Optional[str], + domainRegexSource: Optional[str], + domainRegexFlags: Optional[str], + path: Optional[str], + pathRegexSource: Optional[str], + pathRegexFlags: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clearCookies", + { + "name": name, + "nameRegexSource": nameRegexSource, + "nameRegexFlags": nameRegexFlags, + "domain": domain, + "domainRegexSource": domainRegexSource, + "domainRegexFlags": domainRegexFlags, + "path": path, + "pathRegexSource": pathRegexSource, + "pathRegexFlags": pathRegexFlags, + }, + timeout, + ) + + async def clearPermissions( + self, + timeout: float = 0, + ) -> None: + await self._call("clearPermissions", {}, timeout) + + async def close( + self, + reason: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("close", {"reason": reason}, timeout) + + async def cookies( + self, + urls: Sequence[str], + timeout: float = 0, + ) -> channels.BrowserContextCookiesResult: + result = await self._call("cookies", {"urls": urls}, timeout) + return {"cookies": result["cookies"]} + + async def exposeBinding( + self, + name: str, + noGlobal: Optional[bool], + timeout: float = 0, + ) -> channels.BrowserContextExposeBindingResult: + result = await self._call( + "exposeBinding", {"name": name, "noGlobal": noGlobal}, timeout + ) + return {"disposable": result["disposable"]} + + async def grantPermissions( + self, + permissions: Sequence[str], + origin: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "grantPermissions", {"permissions": permissions, "origin": origin}, timeout + ) + + async def newPage( + self, + timeout: float = 0, + ) -> channels.BrowserContextNewPageResult: + result = await self._call("newPage", {}, timeout) + return {"page": result["page"]} + + async def registerSelectorEngine( + self, + selectorEngine: channels.SelectorEngine, + timeout: float = 0, + ) -> None: + await self._call( + "registerSelectorEngine", {"selectorEngine": selectorEngine}, timeout + ) + + async def setTestIdAttributeName( + self, + testIdAttributeName: str, + timeout: float = 0, + ) -> None: + await self._call( + "setTestIdAttributeName", + {"testIdAttributeName": testIdAttributeName}, + timeout, + ) + + async def setExtraHTTPHeaders( + self, + headers: Sequence[channels.NameValue], + timeout: float = 0, + ) -> None: + await self._call("setExtraHTTPHeaders", {"headers": headers}, timeout) + + async def setGeolocation( + self, + geolocation: Optional[channels.BrowserContextSetGeolocationParamsGeolocation], + timeout: float = 0, + ) -> None: + await self._call("setGeolocation", {"geolocation": geolocation}, timeout) + + async def setHTTPCredentials( + self, + httpCredentials: Optional[Sequence[channels.HttpCredentials]], + timeout: float = 0, + ) -> None: + await self._call( + "setHTTPCredentials", {"httpCredentials": httpCredentials}, timeout + ) + + async def setNetworkInterceptionPatterns( + self, + patterns: Sequence[ + channels.BrowserContextSetNetworkInterceptionPatternsParamsPatterns + ], + timeout: float = 0, + ) -> None: + await self._call( + "setNetworkInterceptionPatterns", {"patterns": patterns}, timeout + ) + + async def setWebSocketInterceptionPatterns( + self, + patterns: Sequence[ + channels.BrowserContextSetWebSocketInterceptionPatternsParamsPatterns + ], + timeout: float = 0, + ) -> None: + await self._call( + "setWebSocketInterceptionPatterns", {"patterns": patterns}, timeout + ) + + async def setOffline( + self, + offline: bool, + timeout: float = 0, + ) -> None: + await self._call("setOffline", {"offline": offline}, timeout) + + async def storageState( + self, + indexedDB: Optional[bool], + opfs: Optional[bool], + credentials: Optional[bool], + timeout: float = 0, + ) -> channels.BrowserContextStorageStateResult: + result = await self._call( + "storageState", + {"indexedDB": indexedDB, "opfs": opfs, "credentials": credentials}, + timeout, + ) + return { + "cookies": result["cookies"], + "origins": result["origins"], + "credentials": result.get("credentials"), + } + + async def setStorageState( + self, + storageState: Optional[ + channels.BrowserContextSetStorageStateParamsStorageState + ], + timeout: float = 0, + ) -> None: + await self._call("setStorageState", {"storageState": storageState}, timeout) + + async def pause( + self, + timeout: float = 0, + ) -> None: + await self._call("pause", {}, timeout) + + async def showRecorder( + self, + language: Optional[str], + mode: Optional[Literal["inspecting", "recording"]], + pauseOnNextStatement: Optional[bool], + testIdAttributeName: Optional[str], + launchOptions: Optional[Any], + contextOptions: Optional[Any], + device: Optional[str], + saveStorage: Optional[str], + outputFile: Optional[str], + handleSIGINT: Optional[bool], + omitCallTracking: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "showRecorder", + { + "language": language, + "mode": mode, + "pauseOnNextStatement": pauseOnNextStatement, + "testIdAttributeName": testIdAttributeName, + "launchOptions": launchOptions, + "contextOptions": contextOptions, + "device": device, + "saveStorage": saveStorage, + "outputFile": outputFile, + "handleSIGINT": handleSIGINT, + "omitCallTracking": omitCallTracking, + }, + timeout, + ) + + async def startRecording( + self, + language: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("startRecording", {"language": language}, timeout) + + async def stopRecording( + self, + timeout: float = 0, + ) -> None: + await self._call("stopRecording", {}, timeout) + + async def exposeConsoleApi( + self, + timeout: float = 0, + ) -> None: + await self._call("exposeConsoleApi", {}, timeout) + + async def newCDPSession( + self, + page: Optional[PageChannel], + frame: Optional[FrameChannel], + timeout: float = 0, + ) -> channels.BrowserContextNewCDPSessionResult: + result = await self._call( + "newCDPSession", {"page": page, "frame": frame}, timeout + ) + return {"session": result["session"]} + + async def createTempFiles( + self, + rootDirName: Optional[str], + items: Sequence[channels.BrowserContextCreateTempFilesParamsItems], + timeout: float = 0, + ) -> channels.BrowserContextCreateTempFilesResult: + result = await self._call( + "createTempFiles", {"rootDirName": rootDirName, "items": items}, timeout + ) + return { + "rootDir": result.get("rootDir"), + "writableStreams": result["writableStreams"], + } + + async def updateSubscription( + self, + event: Literal[ + "console", + "dialog", + "dialogClosed", + "request", + "response", + "requestFinished", + "requestFailed", + ], + enabled: bool, + timeout: float = 0, + ) -> None: + await self._call( + "updateSubscription", {"event": event, "enabled": enabled}, timeout + ) + + async def clockFastForward( + self, + ticksNumber: Optional[float], + ticksString: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clockFastForward", + {"ticksNumber": ticksNumber, "ticksString": ticksString}, + timeout, + ) + + async def clockInstall( + self, + timeNumber: Optional[float], + timeString: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clockInstall", + {"timeNumber": timeNumber, "timeString": timeString}, + timeout, + ) + + async def clockPauseAt( + self, + timeNumber: Optional[float], + timeString: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clockPauseAt", + {"timeNumber": timeNumber, "timeString": timeString}, + timeout, + ) + + async def clockResume( + self, + timeout: float = 0, + ) -> None: + await self._call("clockResume", {}, timeout) + + async def clockRunFor( + self, + ticksNumber: Optional[float], + ticksString: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clockRunFor", + {"ticksNumber": ticksNumber, "ticksString": ticksString}, + timeout, + ) + + async def clockSetFixedTime( + self, + timeNumber: Optional[float], + timeString: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clockSetFixedTime", + {"timeNumber": timeNumber, "timeString": timeString}, + timeout, + ) + + async def clockSetSystemTime( + self, + timeNumber: Optional[float], + timeString: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "clockSetSystemTime", + {"timeNumber": timeNumber, "timeString": timeString}, + timeout, + ) + + async def credentialsInstall( + self, + timeout: float = 0, + ) -> None: + await self._call("credentialsInstall", {}, timeout) + + async def credentialsCreate( + self, + rpId: str, + id: Optional[str], + userHandle: Optional[str], + privateKey: Optional[str], + publicKey: Optional[str], + signCount: Optional[int], + timeout: float = 0, + ) -> channels.BrowserContextCredentialsCreateResult: + result = await self._call( + "credentialsCreate", + { + "rpId": rpId, + "id": id, + "userHandle": userHandle, + "privateKey": privateKey, + "publicKey": publicKey, + "signCount": signCount, + }, + timeout, + ) + return {"credential": result["credential"]} + + async def credentialsGet( + self, + rpId: Optional[str], + id: Optional[str], + timeout: float = 0, + ) -> channels.BrowserContextCredentialsGetResult: + result = await self._call("credentialsGet", {"rpId": rpId, "id": id}, timeout) + return {"credentials": result["credentials"]} + + async def credentialsDelete( + self, + id: str, + timeout: float = 0, + ) -> None: + await self._call("credentialsDelete", {"id": id}, timeout) + + def onBindingCall( + self, handler: Callable[[channels.BrowserContextBindingCallEvent], Any] + ) -> None: + self._add_event_listener( + "bindingCall", handler, lambda event: handler({"binding": event["binding"]}) + ) + + def offBindingCall( + self, handler: Callable[[channels.BrowserContextBindingCallEvent], Any] + ) -> None: + self._remove_event_listener("bindingCall", handler) + + def onConsole( + self, handler: Callable[[channels.BrowserContextConsoleEvent], Any] + ) -> None: + self._add_event_listener( + "console", + handler, + lambda event: handler( + { + "type": event["type"], + "text": event["text"], + "args": event["args"], + "location": event["location"], + "timestamp": event["timestamp"], + "page": event.get("page"), + "worker": event.get("worker"), + } + ), + ) + + def offConsole( + self, handler: Callable[[channels.BrowserContextConsoleEvent], Any] + ) -> None: + self._remove_event_listener("console", handler) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + def onDialog( + self, handler: Callable[[channels.BrowserContextDialogEvent], Any] + ) -> None: + self._add_event_listener( + "dialog", handler, lambda event: handler({"dialog": event["dialog"]}) + ) + + def offDialog( + self, handler: Callable[[channels.BrowserContextDialogEvent], Any] + ) -> None: + self._remove_event_listener("dialog", handler) + + def onDialogClosed( + self, handler: Callable[[channels.BrowserContextDialogClosedEvent], Any] + ) -> None: + self._add_event_listener( + "dialogClosed", handler, lambda event: handler({"dialog": event["dialog"]}) + ) + + def offDialogClosed( + self, handler: Callable[[channels.BrowserContextDialogClosedEvent], Any] + ) -> None: + self._remove_event_listener("dialogClosed", handler) + + def onPage( + self, handler: Callable[[channels.BrowserContextPageEvent], Any] + ) -> None: + self._add_event_listener( + "page", handler, lambda event: handler({"page": event["page"]}) + ) + + def offPage( + self, handler: Callable[[channels.BrowserContextPageEvent], Any] + ) -> None: + self._remove_event_listener("page", handler) + + def onPageError( + self, handler: Callable[[channels.BrowserContextPageErrorEvent], Any] + ) -> None: + self._add_event_listener( + "pageError", + handler, + lambda event: handler( + { + "error": event["error"], + "page": event["page"], + "location": event["location"], + } + ), + ) + + def offPageError( + self, handler: Callable[[channels.BrowserContextPageErrorEvent], Any] + ) -> None: + self._remove_event_listener("pageError", handler) + + def onRoute( + self, handler: Callable[[channels.BrowserContextRouteEvent], Any] + ) -> None: + self._add_event_listener( + "route", handler, lambda event: handler({"route": event["route"]}) + ) + + def offRoute( + self, handler: Callable[[channels.BrowserContextRouteEvent], Any] + ) -> None: + self._remove_event_listener("route", handler) + + def onWebSocketRoute( + self, handler: Callable[[channels.BrowserContextWebSocketRouteEvent], Any] + ) -> None: + self._add_event_listener( + "webSocketRoute", + handler, + lambda event: handler({"webSocketRoute": event["webSocketRoute"]}), + ) + + def offWebSocketRoute( + self, handler: Callable[[channels.BrowserContextWebSocketRouteEvent], Any] + ) -> None: + self._remove_event_listener("webSocketRoute", handler) + + def onServiceWorker( + self, handler: Callable[[channels.BrowserContextServiceWorkerEvent], Any] + ) -> None: + self._add_event_listener( + "serviceWorker", handler, lambda event: handler({"worker": event["worker"]}) + ) + + def offServiceWorker( + self, handler: Callable[[channels.BrowserContextServiceWorkerEvent], Any] + ) -> None: + self._remove_event_listener("serviceWorker", handler) + + def onRequest( + self, handler: Callable[[channels.BrowserContextRequestEvent], Any] + ) -> None: + self._add_event_listener( + "request", + handler, + lambda event: handler( + {"request": event["request"], "page": event.get("page")} + ), + ) + + def offRequest( + self, handler: Callable[[channels.BrowserContextRequestEvent], Any] + ) -> None: + self._remove_event_listener("request", handler) + + def onRequestFailed( + self, handler: Callable[[channels.BrowserContextRequestFailedEvent], Any] + ) -> None: + self._add_event_listener( + "requestFailed", + handler, + lambda event: handler( + { + "request": event["request"], + "failureText": event.get("failureText"), + "responseEndTiming": event["responseEndTiming"], + "page": event.get("page"), + } + ), + ) + + def offRequestFailed( + self, handler: Callable[[channels.BrowserContextRequestFailedEvent], Any] + ) -> None: + self._remove_event_listener("requestFailed", handler) + + def onRequestFinished( + self, handler: Callable[[channels.BrowserContextRequestFinishedEvent], Any] + ) -> None: + self._add_event_listener( + "requestFinished", + handler, + lambda event: handler( + { + "request": event["request"], + "response": event.get("response"), + "responseEndTiming": event["responseEndTiming"], + "page": event.get("page"), + } + ), + ) + + def offRequestFinished( + self, handler: Callable[[channels.BrowserContextRequestFinishedEvent], Any] + ) -> None: + self._remove_event_listener("requestFinished", handler) + + def onResponse( + self, handler: Callable[[channels.BrowserContextResponseEvent], Any] + ) -> None: + self._add_event_listener( + "response", + handler, + lambda event: handler( + {"response": event["response"], "page": event.get("page")} + ), + ) + + def offResponse( + self, handler: Callable[[channels.BrowserContextResponseEvent], Any] + ) -> None: + self._remove_event_listener("response", handler) + + def onRecorderEvent( + self, handler: Callable[[channels.BrowserContextRecorderEventEvent], Any] + ) -> None: + self._add_event_listener( + "recorderEvent", + handler, + lambda event: handler( + { + "event": event["event"], + "data": event["data"], + "page": event["page"], + "code": event["code"], + } + ), + ) + + def offRecorderEvent( + self, handler: Callable[[channels.BrowserContextRecorderEventEvent], Any] + ) -> None: + self._remove_event_listener("recorderEvent", handler) + + +class BrowserTypeChannel(Channel): + async def launch( + self, + channel: Optional[str], + executablePath: Optional[str], + args: Optional[Sequence[str]], + ignoreAllDefaultArgs: Optional[bool], + ignoreDefaultArgs: Optional[Sequence[str]], + handleSIGINT: Optional[bool], + handleSIGTERM: Optional[bool], + handleSIGHUP: Optional[bool], + env: Optional[Sequence[channels.NameValue]], + headless: Optional[bool], + proxy: Optional[channels.BrowserTypeLaunchParamsProxy], + downloadsPath: Optional[str], + tracesDir: Optional[str], + artifactsDir: Optional[str], + chromiumSandbox: Optional[bool], + firefoxUserPrefs: Optional[Any], + cdpPort: Optional[int], + slowMo: Optional[float], + timeout: float = 0, + ) -> channels.BrowserTypeLaunchResult: + result = await self._call( + "launch", + { + "channel": channel, + "executablePath": executablePath, + "args": args, + "ignoreAllDefaultArgs": ignoreAllDefaultArgs, + "ignoreDefaultArgs": ignoreDefaultArgs, + "handleSIGINT": handleSIGINT, + "handleSIGTERM": handleSIGTERM, + "handleSIGHUP": handleSIGHUP, + "env": env, + "headless": headless, + "proxy": proxy, + "downloadsPath": downloadsPath, + "tracesDir": tracesDir, + "artifactsDir": artifactsDir, + "chromiumSandbox": chromiumSandbox, + "firefoxUserPrefs": firefoxUserPrefs, + "cdpPort": cdpPort, + "slowMo": slowMo, + }, + timeout, + ) + return {"browser": result["browser"]} + + async def launchPersistentContext( + self, + channel: Optional[str], + executablePath: Optional[str], + args: Optional[Sequence[str]], + ignoreAllDefaultArgs: Optional[bool], + ignoreDefaultArgs: Optional[Sequence[str]], + handleSIGINT: Optional[bool], + handleSIGTERM: Optional[bool], + handleSIGHUP: Optional[bool], + env: Optional[Sequence[channels.NameValue]], + headless: Optional[bool], + proxy: Optional[channels.BrowserTypeLaunchPersistentContextParamsProxy], + downloadsPath: Optional[str], + tracesDir: Optional[str], + artifactsDir: Optional[str], + chromiumSandbox: Optional[bool], + firefoxUserPrefs: Optional[Any], + cdpPort: Optional[int], + noDefaultViewport: Optional[bool], + viewport: Optional[channels.BrowserTypeLaunchPersistentContextParamsViewport], + screen: Optional[channels.BrowserTypeLaunchPersistentContextParamsScreen], + ignoreHTTPSErrors: Optional[bool], + clientCertificates: Optional[ + Sequence[ + channels.BrowserTypeLaunchPersistentContextParamsClientCertificates + ] + ], + javaScriptEnabled: Optional[bool], + bypassCSP: Optional[bool], + userAgent: Optional[str], + locale: Optional[str], + timezoneId: Optional[str], + geolocation: Optional[ + channels.BrowserTypeLaunchPersistentContextParamsGeolocation + ], + permissions: Optional[Sequence[str]], + extraHTTPHeaders: Optional[Sequence[channels.NameValue]], + offline: Optional[bool], + httpCredentials: Optional[Sequence[channels.HttpCredentials]], + deviceScaleFactor: Optional[float], + isMobile: Optional[bool], + hasTouch: Optional[bool], + colorScheme: Optional[Literal["dark", "light", "no-preference", "no-override"]], + reducedMotion: Optional[Literal["reduce", "no-preference", "no-override"]], + forcedColors: Optional[Literal["active", "none", "no-override"]], + acceptDownloads: Optional[ + Literal["accept", "deny", "internal-browser-default"] + ], + contrast: Optional[Literal["no-preference", "more", "no-override"]], + baseURL: Optional[str], + recordVideo: Optional[ + channels.BrowserTypeLaunchPersistentContextParamsRecordVideo + ], + strictSelectors: Optional[bool], + serviceWorkers: Optional[Literal["allow", "block"]], + selectorEngines: Optional[Sequence[channels.SelectorEngine]], + testIdAttributeName: Optional[str], + userDataDir: str, + slowMo: Optional[float], + timeout: float = 0, + ) -> channels.BrowserTypeLaunchPersistentContextResult: + result = await self._call( + "launchPersistentContext", + { + "channel": channel, + "executablePath": executablePath, + "args": args, + "ignoreAllDefaultArgs": ignoreAllDefaultArgs, + "ignoreDefaultArgs": ignoreDefaultArgs, + "handleSIGINT": handleSIGINT, + "handleSIGTERM": handleSIGTERM, + "handleSIGHUP": handleSIGHUP, + "env": env, + "headless": headless, + "proxy": proxy, + "downloadsPath": downloadsPath, + "tracesDir": tracesDir, + "artifactsDir": artifactsDir, + "chromiumSandbox": chromiumSandbox, + "firefoxUserPrefs": firefoxUserPrefs, + "cdpPort": cdpPort, + "noDefaultViewport": noDefaultViewport, + "viewport": viewport, + "screen": screen, + "ignoreHTTPSErrors": ignoreHTTPSErrors, + "clientCertificates": clientCertificates, + "javaScriptEnabled": javaScriptEnabled, + "bypassCSP": bypassCSP, + "userAgent": userAgent, + "locale": locale, + "timezoneId": timezoneId, + "geolocation": geolocation, + "permissions": permissions, + "extraHTTPHeaders": extraHTTPHeaders, + "offline": offline, + "httpCredentials": httpCredentials, + "deviceScaleFactor": deviceScaleFactor, + "isMobile": isMobile, + "hasTouch": hasTouch, + "colorScheme": colorScheme, + "reducedMotion": reducedMotion, + "forcedColors": forcedColors, + "acceptDownloads": acceptDownloads, + "contrast": contrast, + "baseURL": baseURL, + "recordVideo": recordVideo, + "strictSelectors": strictSelectors, + "serviceWorkers": serviceWorkers, + "selectorEngines": selectorEngines, + "testIdAttributeName": testIdAttributeName, + "userDataDir": userDataDir, + "slowMo": slowMo, + }, + timeout, + ) + return {"browser": result["browser"], "context": result["context"]} + + async def connectOverCDP( + self, + endpointURL: Optional[str], + headers: Optional[Sequence[channels.NameValue]], + slowMo: Optional[float], + isLocal: Optional[bool], + noDefaults: Optional[bool], + isWebView: Optional[bool], + artifactsDir: Optional[str], + transport: Optional[str], + timeout: float = 0, + ) -> channels.BrowserTypeConnectOverCDPResult: + result = await self._call( + "connectOverCDP", + { + "endpointURL": endpointURL, + "headers": headers, + "slowMo": slowMo, + "isLocal": isLocal, + "noDefaults": noDefaults, + "isWebView": isWebView, + "artifactsDir": artifactsDir, + "transport": transport, + }, + timeout, + ) + return { + "browser": result["browser"], + "defaultContext": result.get("defaultContext"), + } + + async def connectToWorker( + self, + endpoint: str, + timeout: float = 0, + ) -> channels.BrowserTypeConnectToWorkerResult: + result = await self._call("connectToWorker", {"endpoint": endpoint}, timeout) + return {"worker": result["worker"]} + + +class DisposableChannel(Channel): + async def dispose( + self, + timeout: float = 0, + ) -> None: + await self._call("dispose", {}, timeout) + + +class ElectronChannel(Channel): + async def launch( + self, + executablePath: Optional[str], + args: Optional[Sequence[str]], + chromiumSandbox: Optional[bool], + cwd: Optional[str], + env: Optional[Sequence[channels.NameValue]], + acceptDownloads: Optional[ + Literal["accept", "deny", "internal-browser-default"] + ], + bypassCSP: Optional[bool], + colorScheme: Optional[Literal["dark", "light", "no-preference", "no-override"]], + extraHTTPHeaders: Optional[Sequence[channels.NameValue]], + geolocation: Optional[channels.ElectronLaunchParamsGeolocation], + httpCredentials: Optional[Sequence[channels.HttpCredentials]], + ignoreHTTPSErrors: Optional[bool], + locale: Optional[str], + offline: Optional[bool], + recordVideo: Optional[channels.ElectronLaunchParamsRecordVideo], + strictSelectors: Optional[bool], + timezoneId: Optional[str], + tracesDir: Optional[str], + artifactsDir: Optional[str], + selectorEngines: Optional[Sequence[channels.SelectorEngine]], + testIdAttributeName: Optional[str], + timeout: float = 0, + ) -> channels.ElectronLaunchResult: + result = await self._call( + "launch", + { + "executablePath": executablePath, + "args": args, + "chromiumSandbox": chromiumSandbox, + "cwd": cwd, + "env": env, + "acceptDownloads": acceptDownloads, + "bypassCSP": bypassCSP, + "colorScheme": colorScheme, + "extraHTTPHeaders": extraHTTPHeaders, + "geolocation": geolocation, + "httpCredentials": httpCredentials, + "ignoreHTTPSErrors": ignoreHTTPSErrors, + "locale": locale, + "offline": offline, + "recordVideo": recordVideo, + "strictSelectors": strictSelectors, + "timezoneId": timezoneId, + "tracesDir": tracesDir, + "artifactsDir": artifactsDir, + "selectorEngines": selectorEngines, + "testIdAttributeName": testIdAttributeName, + }, + timeout, + ) + return {"electronApplication": result["electronApplication"]} + + +class ElectronApplicationChannel(Channel): + async def browserWindow( + self, + page: PageChannel, + timeout: float = 0, + ) -> channels.ElectronApplicationBrowserWindowResult: + result = await self._call("browserWindow", {"page": page}, timeout) + return {"handle": result["handle"]} + + async def evaluateExpression( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.ElectronApplicationEvaluateExpressionResult: + result = await self._call( + "evaluateExpression", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"value": result["value"]} + + async def evaluateExpressionHandle( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.ElectronApplicationEvaluateExpressionHandleResult: + result = await self._call( + "evaluateExpressionHandle", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"handle": result["handle"]} + + async def updateSubscription( + self, + event: Literal["console"], + enabled: bool, + timeout: float = 0, + ) -> None: + await self._call( + "updateSubscription", {"event": event, "enabled": enabled}, timeout + ) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + def onConsole( + self, handler: Callable[[channels.ElectronApplicationConsoleEvent], Any] + ) -> None: + self._add_event_listener( + "console", + handler, + lambda event: handler( + { + "type": event["type"], + "text": event["text"], + "args": event["args"], + "location": event["location"], + "timestamp": event["timestamp"], + } + ), + ) + + def offConsole( + self, handler: Callable[[channels.ElectronApplicationConsoleEvent], Any] + ) -> None: + self._remove_event_listener("console", handler) + + +class FrameChannel(Channel): + async def evalOnSelector( + self, + selector: str, + strict: Optional[bool], + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.FrameEvalOnSelectorResult: + result = await self._call( + "evalOnSelector", + { + "selector": selector, + "strict": strict, + "expression": expression, + "isFunction": isFunction, + "arg": arg, + }, + timeout, + ) + return {"value": result["value"]} + + async def evalOnSelectorAll( + self, + selector: str, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.FrameEvalOnSelectorAllResult: + result = await self._call( + "evalOnSelectorAll", + { + "selector": selector, + "expression": expression, + "isFunction": isFunction, + "arg": arg, + }, + timeout, + ) + return {"value": result["value"]} + + async def addScriptTag( + self, + url: Optional[str], + content: Optional[str], + type: Optional[str], + timeout: float = 0, + ) -> channels.FrameAddScriptTagResult: + result = await self._call( + "addScriptTag", {"url": url, "content": content, "type": type}, timeout + ) + return {"element": result["element"]} + + async def addStyleTag( + self, + url: Optional[str], + content: Optional[str], + timeout: float = 0, + ) -> channels.FrameAddStyleTagResult: + result = await self._call( + "addStyleTag", {"url": url, "content": content}, timeout + ) + return {"element": result["element"]} + + async def ariaSnapshot( + self, + mode: Optional[Literal["ai", "default"]], + selector: Optional[str], + depth: Optional[int], + boxes: Optional[bool], + timeout: float = 0, + ) -> channels.FrameAriaSnapshotResult: + result = await self._call( + "ariaSnapshot", + {"mode": mode, "selector": selector, "depth": depth, "boxes": boxes}, + timeout, + ) + return {"snapshot": result["snapshot"]} + + async def ariaSnapshotJSON( + self, + mode: Optional[Literal["ai", "default"]], + selector: Optional[str], + depth: Optional[int], + boxes: Optional[bool], + timeout: float = 0, + ) -> channels.FrameAriaSnapshotJSONResult: + result = await self._call( + "ariaSnapshotJSON", + {"mode": mode, "selector": selector, "depth": depth, "boxes": boxes}, + timeout, + ) + return {"snapshot": result["snapshot"]} + + async def blur( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call("blur", {"selector": selector, "strict": strict}, timeout) + + async def check( + self, + selector: str, + strict: Optional[bool], + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "check", + { + "selector": selector, + "strict": strict, + "force": force, + "scroll": scroll, + "position": position, + "trial": trial, + }, + timeout, + ) + + async def click( + self, + selector: str, + strict: Optional[bool], + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + noWaitAfter: Optional[bool], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + delay: Optional[float], + button: Optional[Literal["left", "right", "middle"]], + clickCount: Optional[int], + trial: Optional[bool], + steps: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "click", + { + "selector": selector, + "strict": strict, + "force": force, + "scroll": scroll, + "noWaitAfter": noWaitAfter, + "modifiers": modifiers, + "position": position, + "delay": delay, + "button": button, + "clickCount": clickCount, + "trial": trial, + "steps": steps, + }, + timeout, + ) + + async def content( + self, + includeShadow: Optional[bool], + timeout: float = 0, + ) -> channels.FrameContentResult: + result = await self._call("content", {"includeShadow": includeShadow}, timeout) + return {"value": result["value"]} + + async def dragAndDrop( + self, + source: str, + target: str, + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + trial: Optional[bool], + sourcePosition: Optional[channels.Point], + targetPosition: Optional[channels.Point], + strict: Optional[bool], + steps: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "dragAndDrop", + { + "source": source, + "target": target, + "force": force, + "scroll": scroll, + "trial": trial, + "sourcePosition": sourcePosition, + "targetPosition": targetPosition, + "strict": strict, + "steps": steps, + }, + timeout, + ) + + async def drop( + self, + selector: str, + strict: Optional[bool], + position: Optional[channels.Point], + payloads: Optional[Sequence[channels.FrameDropParamsPayloads]], + localPaths: Optional[Sequence[str]], + streams: Optional[Sequence[WritableStreamChannel]], + data: Optional[Sequence[channels.FrameDropParamsData]], + timeout: float = 0, + ) -> None: + await self._call( + "drop", + { + "selector": selector, + "strict": strict, + "position": position, + "payloads": payloads, + "localPaths": localPaths, + "streams": streams, + "data": data, + }, + timeout, + ) + + async def dblclick( + self, + selector: str, + strict: Optional[bool], + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + delay: Optional[float], + button: Optional[Literal["left", "right", "middle"]], + trial: Optional[bool], + steps: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "dblclick", + { + "selector": selector, + "strict": strict, + "force": force, + "scroll": scroll, + "modifiers": modifiers, + "position": position, + "delay": delay, + "button": button, + "trial": trial, + "steps": steps, + }, + timeout, + ) + + async def dispatchEvent( + self, + selector: str, + strict: Optional[bool], + type: str, + eventInit: channels.SerializedArgument, + timeout: float = 0, + ) -> None: + await self._call( + "dispatchEvent", + { + "selector": selector, + "strict": strict, + "type": type, + "eventInit": eventInit, + }, + timeout, + ) + + async def evaluateExpression( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.FrameEvaluateExpressionResult: + result = await self._call( + "evaluateExpression", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"value": result["value"]} + + async def evaluateExpressionHandle( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.FrameEvaluateExpressionHandleResult: + result = await self._call( + "evaluateExpressionHandle", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"handle": result["handle"]} + + async def fill( + self, + selector: str, + strict: Optional[bool], + value: str, + force: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "fill", + {"selector": selector, "strict": strict, "value": value, "force": force}, + timeout, + ) + + async def focus( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call("focus", {"selector": selector, "strict": strict}, timeout) + + async def frameElement( + self, + timeout: float = 0, + ) -> channels.FrameFrameElementResult: + result = await self._call("frameElement", {}, timeout) + return {"element": result["element"]} + + async def resolveSelector( + self, + selector: str, + sdkLanguage: Optional[channels.SDKLanguage], + timeout: float = 0, + ) -> channels.FrameResolveSelectorResult: + result = await self._call( + "resolveSelector", + {"selector": selector, "sdkLanguage": sdkLanguage}, + timeout, + ) + return { + "resolvedSelector": result["resolvedSelector"], + "locatorCode": result.get("locatorCode"), + } + + async def highlight( + self, + selector: str, + style: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("highlight", {"selector": selector, "style": style}, timeout) + + async def hideHighlight( + self, + selector: str, + timeout: float = 0, + ) -> None: + await self._call("hideHighlight", {"selector": selector}, timeout) + + async def getAttribute( + self, + selector: str, + strict: Optional[bool], + name: str, + timeout: float = 0, + ) -> channels.FrameGetAttributeResult: + result = await self._call( + "getAttribute", + {"selector": selector, "strict": strict, "name": name}, + timeout, + ) + return {"value": result.get("value")} + + async def goto( + self, + url: str, + waitUntil: Optional[channels.LifecycleEvent], + referer: Optional[str], + timeout: float = 0, + ) -> channels.FrameGotoResult: + result = await self._call( + "goto", {"url": url, "waitUntil": waitUntil, "referer": referer}, timeout + ) + return {"response": result.get("response")} + + async def hover( + self, + selector: str, + strict: Optional[bool], + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "hover", + { + "selector": selector, + "strict": strict, + "force": force, + "scroll": scroll, + "modifiers": modifiers, + "position": position, + "trial": trial, + }, + timeout, + ) + + async def innerHTML( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameInnerHTMLResult: + result = await self._call( + "innerHTML", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def innerText( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameInnerTextResult: + result = await self._call( + "innerText", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def inputValue( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameInputValueResult: + result = await self._call( + "inputValue", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def isChecked( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameIsCheckedResult: + result = await self._call( + "isChecked", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def isDisabled( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameIsDisabledResult: + result = await self._call( + "isDisabled", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def isEnabled( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameIsEnabledResult: + result = await self._call( + "isEnabled", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def isHidden( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameIsHiddenResult: + result = await self._call( + "isHidden", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def isVisible( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameIsVisibleResult: + result = await self._call( + "isVisible", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def isEditable( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameIsEditableResult: + result = await self._call( + "isEditable", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result["value"]} + + async def press( + self, + selector: str, + strict: Optional[bool], + key: str, + delay: Optional[float], + noWaitAfter: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "press", + { + "selector": selector, + "strict": strict, + "key": key, + "delay": delay, + "noWaitAfter": noWaitAfter, + }, + timeout, + ) + + async def querySelector( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameQuerySelectorResult: + result = await self._call( + "querySelector", {"selector": selector, "strict": strict}, timeout + ) + return {"element": result.get("element")} + + async def querySelectorAll( + self, + selector: str, + timeout: float = 0, + ) -> channels.FrameQuerySelectorAllResult: + result = await self._call("querySelectorAll", {"selector": selector}, timeout) + return {"elements": result["elements"]} + + async def queryCount( + self, + selector: str, + timeout: float = 0, + ) -> channels.FrameQueryCountResult: + result = await self._call("queryCount", {"selector": selector}, timeout) + return {"value": result["value"]} + + async def selectOption( + self, + selector: str, + strict: Optional[bool], + elements: Optional[Sequence[ElementHandleChannel]], + options: Optional[Sequence[channels.FrameSelectOptionParamsOptions]], + force: Optional[bool], + timeout: float = 0, + ) -> channels.FrameSelectOptionResult: + result = await self._call( + "selectOption", + { + "selector": selector, + "strict": strict, + "elements": elements, + "options": options, + "force": force, + }, + timeout, + ) + return {"values": result["values"]} + + async def setContent( + self, + html: str, + waitUntil: Optional[channels.LifecycleEvent], + timeout: float = 0, + ) -> None: + await self._call("setContent", {"html": html, "waitUntil": waitUntil}, timeout) + + async def setInputFiles( + self, + selector: str, + strict: Optional[bool], + payloads: Optional[Sequence[channels.FrameSetInputFilesParamsPayloads]], + localDirectory: Optional[str], + directoryStream: Optional[WritableStreamChannel], + localPaths: Optional[Sequence[str]], + streams: Optional[Sequence[WritableStreamChannel]], + timeout: float = 0, + ) -> None: + await self._call( + "setInputFiles", + { + "selector": selector, + "strict": strict, + "payloads": payloads, + "localDirectory": localDirectory, + "directoryStream": directoryStream, + "localPaths": localPaths, + "streams": streams, + }, + timeout, + ) + + async def tap( + self, + selector: str, + strict: Optional[bool], + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "tap", + { + "selector": selector, + "strict": strict, + "force": force, + "scroll": scroll, + "modifiers": modifiers, + "position": position, + "trial": trial, + }, + timeout, + ) + + async def textContent( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameTextContentResult: + result = await self._call( + "textContent", {"selector": selector, "strict": strict}, timeout + ) + return {"value": result.get("value")} + + async def title( + self, + timeout: float = 0, + ) -> channels.FrameTitleResult: + result = await self._call("title", {}, timeout) + return {"value": result["value"]} + + async def type( + self, + selector: str, + strict: Optional[bool], + text: str, + delay: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "type", + {"selector": selector, "strict": strict, "text": text, "delay": delay}, + timeout, + ) + + async def uncheck( + self, + selector: str, + strict: Optional[bool], + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "uncheck", + { + "selector": selector, + "strict": strict, + "force": force, + "scroll": scroll, + "position": position, + "trial": trial, + }, + timeout, + ) + + async def waitForTimeout( + self, + waitTimeout: float, + timeout: float = 0, + ) -> None: + await self._call("waitForTimeout", {"waitTimeout": waitTimeout}, timeout) + + async def waitForFunction( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + pollingInterval: Optional[float], + selector: Optional[str], + strict: Optional[bool], + timeout: float = 0, + ) -> channels.FrameWaitForFunctionResult: + result = await self._call( + "waitForFunction", + { + "expression": expression, + "isFunction": isFunction, + "arg": arg, + "pollingInterval": pollingInterval, + "selector": selector, + "strict": strict, + }, + timeout, + ) + return {"handle": result.get("handle")} + + async def waitForSelector( + self, + selector: str, + strict: Optional[bool], + state: Optional[Literal["attached", "detached", "visible", "hidden"]], + omitReturnValue: Optional[bool], + timeout: float = 0, + ) -> channels.FrameWaitForSelectorResult: + result = await self._call( + "waitForSelector", + { + "selector": selector, + "strict": strict, + "state": state, + "omitReturnValue": omitReturnValue, + }, + timeout, + ) + return {"element": result.get("element")} + + async def expect( + self, + selector: Optional[str], + expression: str, + expressionArg: Optional[Any], + pseudo: Optional[Literal["before", "after"]], + expectedText: Optional[Sequence[channels.ExpectedTextValue]], + expectedNumber: Optional[float], + expectedValue: Optional[channels.SerializedArgument], + useInnerText: Optional[bool], + isNot: bool, + timeout: float = 0, + ) -> None: + await self._call( + "expect", + { + "selector": selector, + "expression": expression, + "expressionArg": expressionArg, + "pseudo": pseudo, + "expectedText": expectedText, + "expectedNumber": expectedNumber, + "expectedValue": expectedValue, + "useInnerText": useInnerText, + "isNot": isNot, + }, + timeout, + ) + + async def webmcpTools( + self, + timeout: float = 0, + ) -> channels.FrameWebmcpToolsResult: + result = await self._call("webmcpTools", {}, timeout) + return {"tools": result["tools"]} + + async def webmcpCallTool( + self, + name: str, + input: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.FrameWebmcpCallToolResult: + result = await self._call( + "webmcpCallTool", {"name": name, "input": input}, timeout + ) + return {"result": result["result"]} + + def onLoadstate( + self, handler: Callable[[channels.FrameLoadstateEvent], Any] + ) -> None: + self._add_event_listener( + "loadstate", + handler, + lambda event: handler( + {"add": event.get("add"), "remove": event.get("remove")} + ), + ) + + def offLoadstate( + self, handler: Callable[[channels.FrameLoadstateEvent], Any] + ) -> None: + self._remove_event_listener("loadstate", handler) + + def onNavigated( + self, handler: Callable[[channels.FrameNavigatedEvent], Any] + ) -> None: + self._add_event_listener( + "navigated", + handler, + lambda event: handler( + { + "url": event["url"], + "name": event["name"], + "newDocument": event.get("newDocument"), + "error": event.get("error"), + } + ), + ) + + def offNavigated( + self, handler: Callable[[channels.FrameNavigatedEvent], Any] + ) -> None: + self._remove_event_listener("navigated", handler) + + +class JSHandleChannel(Channel): + async def dispose( + self, + timeout: float = 0, + ) -> None: + await self._call("dispose", {}, timeout) + + async def evaluateExpression( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.JSHandleEvaluateExpressionResult: + result = await self._call( + "evaluateExpression", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"value": result["value"]} + + async def evaluateExpressionHandle( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.JSHandleEvaluateExpressionHandleResult: + result = await self._call( + "evaluateExpressionHandle", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"handle": result["handle"]} + + async def getPropertyList( + self, + timeout: float = 0, + ) -> channels.JSHandleGetPropertyListResult: + result = await self._call("getPropertyList", {}, timeout) + return {"properties": result["properties"]} + + async def getProperty( + self, + name: str, + timeout: float = 0, + ) -> channels.JSHandleGetPropertyResult: + result = await self._call("getProperty", {"name": name}, timeout) + return {"handle": result["handle"]} + + async def jsonValue( + self, + timeout: float = 0, + ) -> channels.JSHandleJsonValueResult: + result = await self._call("jsonValue", {}, timeout) + return {"value": result["value"]} + + def onPreviewUpdated( + self, handler: Callable[[channels.JSHandlePreviewUpdatedEvent], Any] + ) -> None: + self._add_event_listener( + "previewUpdated", + handler, + lambda event: handler({"preview": event["preview"]}), + ) + + def offPreviewUpdated( + self, handler: Callable[[channels.JSHandlePreviewUpdatedEvent], Any] + ) -> None: + self._remove_event_listener("previewUpdated", handler) + + +class ElementHandleChannel(JSHandleChannel): + async def evalOnSelector( + self, + selector: str, + strict: Optional[bool], + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.ElementHandleEvalOnSelectorResult: + result = await self._call( + "evalOnSelector", + { + "selector": selector, + "strict": strict, + "expression": expression, + "isFunction": isFunction, + "arg": arg, + }, + timeout, + ) + return {"value": result["value"]} + + async def evalOnSelectorAll( + self, + selector: str, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.ElementHandleEvalOnSelectorAllResult: + result = await self._call( + "evalOnSelectorAll", + { + "selector": selector, + "expression": expression, + "isFunction": isFunction, + "arg": arg, + }, + timeout, + ) + return {"value": result["value"]} + + async def boundingBox( + self, + timeout: float = 0, + ) -> channels.ElementHandleBoundingBoxResult: + result = await self._call("boundingBox", {}, timeout) + return {"value": result.get("value")} + + async def check( + self, + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "check", + {"force": force, "scroll": scroll, "position": position, "trial": trial}, + timeout, + ) + + async def click( + self, + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + noWaitAfter: Optional[bool], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + delay: Optional[float], + button: Optional[Literal["left", "right", "middle"]], + clickCount: Optional[int], + trial: Optional[bool], + steps: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "click", + { + "force": force, + "scroll": scroll, + "noWaitAfter": noWaitAfter, + "modifiers": modifiers, + "position": position, + "delay": delay, + "button": button, + "clickCount": clickCount, + "trial": trial, + "steps": steps, + }, + timeout, + ) + + async def contentFrame( + self, + timeout: float = 0, + ) -> channels.ElementHandleContentFrameResult: + result = await self._call("contentFrame", {}, timeout) + return {"frame": result.get("frame")} + + async def dblclick( + self, + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + delay: Optional[float], + button: Optional[Literal["left", "right", "middle"]], + trial: Optional[bool], + steps: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "dblclick", + { + "force": force, + "scroll": scroll, + "modifiers": modifiers, + "position": position, + "delay": delay, + "button": button, + "trial": trial, + "steps": steps, + }, + timeout, + ) + + async def dispatchEvent( + self, + type: str, + eventInit: channels.SerializedArgument, + timeout: float = 0, + ) -> None: + await self._call( + "dispatchEvent", {"type": type, "eventInit": eventInit}, timeout + ) + + async def fill( + self, + value: str, + force: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call("fill", {"value": value, "force": force}, timeout) + + async def focus( + self, + timeout: float = 0, + ) -> None: + await self._call("focus", {}, timeout) + + async def getAttribute( + self, + name: str, + timeout: float = 0, + ) -> channels.ElementHandleGetAttributeResult: + result = await self._call("getAttribute", {"name": name}, timeout) + return {"value": result.get("value")} + + async def hover( + self, + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "hover", + { + "force": force, + "scroll": scroll, + "modifiers": modifiers, + "position": position, + "trial": trial, + }, + timeout, + ) + + async def innerHTML( + self, + timeout: float = 0, + ) -> channels.ElementHandleInnerHTMLResult: + result = await self._call("innerHTML", {}, timeout) + return {"value": result["value"]} + + async def innerText( + self, + timeout: float = 0, + ) -> channels.ElementHandleInnerTextResult: + result = await self._call("innerText", {}, timeout) + return {"value": result["value"]} + + async def inputValue( + self, + timeout: float = 0, + ) -> channels.ElementHandleInputValueResult: + result = await self._call("inputValue", {}, timeout) + return {"value": result["value"]} + + async def isChecked( + self, + timeout: float = 0, + ) -> channels.ElementHandleIsCheckedResult: + result = await self._call("isChecked", {}, timeout) + return {"value": result["value"]} + + async def isDisabled( + self, + timeout: float = 0, + ) -> channels.ElementHandleIsDisabledResult: + result = await self._call("isDisabled", {}, timeout) + return {"value": result["value"]} + + async def isEditable( + self, + timeout: float = 0, + ) -> channels.ElementHandleIsEditableResult: + result = await self._call("isEditable", {}, timeout) + return {"value": result["value"]} + + async def isEnabled( + self, + timeout: float = 0, + ) -> channels.ElementHandleIsEnabledResult: + result = await self._call("isEnabled", {}, timeout) + return {"value": result["value"]} + + async def isHidden( + self, + timeout: float = 0, + ) -> channels.ElementHandleIsHiddenResult: + result = await self._call("isHidden", {}, timeout) + return {"value": result["value"]} + + async def isVisible( + self, + timeout: float = 0, + ) -> channels.ElementHandleIsVisibleResult: + result = await self._call("isVisible", {}, timeout) + return {"value": result["value"]} + + async def ownerFrame( + self, + timeout: float = 0, + ) -> channels.ElementHandleOwnerFrameResult: + result = await self._call("ownerFrame", {}, timeout) + return {"frame": result.get("frame")} + + async def press( + self, + key: str, + delay: Optional[float], + noWaitAfter: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "press", {"key": key, "delay": delay, "noWaitAfter": noWaitAfter}, timeout + ) + + async def querySelector( + self, + selector: str, + strict: Optional[bool], + timeout: float = 0, + ) -> channels.ElementHandleQuerySelectorResult: + result = await self._call( + "querySelector", {"selector": selector, "strict": strict}, timeout + ) + return {"element": result.get("element")} + + async def querySelectorAll( + self, + selector: str, + timeout: float = 0, + ) -> channels.ElementHandleQuerySelectorAllResult: + result = await self._call("querySelectorAll", {"selector": selector}, timeout) + return {"elements": result["elements"]} + + async def screenshot( + self, + type: Optional[Literal["png", "jpeg", "webp"]], + quality: Optional[int], + omitBackground: Optional[bool], + caret: Optional[Literal["hide", "initial"]], + animations: Optional[Literal["disabled", "allow"]], + scale: Optional[Literal["css", "device"]], + mask: Optional[Sequence[channels.ElementHandleScreenshotParamsMask]], + maskColor: Optional[str], + style: Optional[str], + timeout: float = 0, + ) -> channels.ElementHandleScreenshotResult: + result = await self._call( + "screenshot", + { + "type": type, + "quality": quality, + "omitBackground": omitBackground, + "caret": caret, + "animations": animations, + "scale": scale, + "mask": mask, + "maskColor": maskColor, + "style": style, + }, + timeout, + ) + return {"binary": result["binary"]} + + async def scrollIntoViewIfNeeded( + self, + timeout: float = 0, + ) -> None: + await self._call("scrollIntoViewIfNeeded", {}, timeout) + + async def selectOption( + self, + elements: Optional[Sequence[ElementHandleChannel]], + options: Optional[Sequence[channels.ElementHandleSelectOptionParamsOptions]], + force: Optional[bool], + timeout: float = 0, + ) -> channels.ElementHandleSelectOptionResult: + result = await self._call( + "selectOption", + {"elements": elements, "options": options, "force": force}, + timeout, + ) + return {"values": result["values"]} + + async def selectText( + self, + force: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call("selectText", {"force": force}, timeout) + + async def setInputFiles( + self, + payloads: Optional[Sequence[channels.ElementHandleSetInputFilesParamsPayloads]], + localDirectory: Optional[str], + directoryStream: Optional[WritableStreamChannel], + localPaths: Optional[Sequence[str]], + streams: Optional[Sequence[WritableStreamChannel]], + timeout: float = 0, + ) -> None: + await self._call( + "setInputFiles", + { + "payloads": payloads, + "localDirectory": localDirectory, + "directoryStream": directoryStream, + "localPaths": localPaths, + "streams": streams, + }, + timeout, + ) + + async def tap( + self, + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + modifiers: Optional[ + Sequence[Literal["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]] + ], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "tap", + { + "force": force, + "scroll": scroll, + "modifiers": modifiers, + "position": position, + "trial": trial, + }, + timeout, + ) + + async def textContent( + self, + timeout: float = 0, + ) -> channels.ElementHandleTextContentResult: + result = await self._call("textContent", {}, timeout) + return {"value": result.get("value")} + + async def type( + self, + text: str, + delay: Optional[float], + timeout: float = 0, + ) -> None: + await self._call("type", {"text": text, "delay": delay}, timeout) + + async def uncheck( + self, + force: Optional[bool], + scroll: Optional[Literal["auto", "none"]], + position: Optional[channels.Point], + trial: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "uncheck", + {"force": force, "scroll": scroll, "position": position, "trial": trial}, + timeout, + ) + + async def waitForElementState( + self, + state: Literal[ + "visible", "hidden", "stable", "enabled", "disabled", "editable" + ], + timeout: float = 0, + ) -> None: + await self._call("waitForElementState", {"state": state}, timeout) + + async def waitForSelector( + self, + selector: str, + strict: Optional[bool], + state: Optional[Literal["attached", "detached", "visible", "hidden"]], + timeout: float = 0, + ) -> channels.ElementHandleWaitForSelectorResult: + result = await self._call( + "waitForSelector", + {"selector": selector, "strict": strict, "state": state}, + timeout, + ) + return {"element": result.get("element")} + + +class LocalUtilsChannel(Channel): + async def zip( + self, + zipFile: str, + entries: Sequence[channels.NameValue], + stacksId: Optional[str], + mode: Literal["write", "append"], + includeSources: bool, + additionalSources: Optional[Sequence[str]], + timeout: float = 0, + ) -> None: + await self._call( + "zip", + { + "zipFile": zipFile, + "entries": entries, + "stacksId": stacksId, + "mode": mode, + "includeSources": includeSources, + "additionalSources": additionalSources, + }, + timeout, + ) + + async def harOpen( + self, + file: str, + timeout: float = 0, + ) -> channels.LocalUtilsHarOpenResult: + result = await self._call("harOpen", {"file": file}, timeout) + return {"harId": result.get("harId"), "error": result.get("error")} + + async def harLookup( + self, + harId: str, + url: str, + method: str, + headers: Sequence[channels.NameValue], + postData: Optional[str], + isNavigationRequest: bool, + timeout: float = 0, + ) -> channels.LocalUtilsHarLookupResult: + result = await self._call( + "harLookup", + { + "harId": harId, + "url": url, + "method": method, + "headers": headers, + "postData": postData, + "isNavigationRequest": isNavigationRequest, + }, + timeout, + ) + return { + "action": result["action"], + "message": result.get("message"), + "redirectURL": result.get("redirectURL"), + "status": result.get("status"), + "headers": result.get("headers"), + "body": result.get("body"), + } + + async def harClose( + self, + harId: str, + timeout: float = 0, + ) -> None: + await self._call("harClose", {"harId": harId}, timeout) + + async def harUnzip( + self, + zipFile: str, + harFile: str, + resourcesDir: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "harUnzip", + {"zipFile": zipFile, "harFile": harFile, "resourcesDir": resourcesDir}, + timeout, + ) + + async def connect( + self, + endpoint: str, + headers: Optional[Any], + exposeNetwork: Optional[str], + slowMo: Optional[float], + socksProxyRedirectPortForTest: Optional[int], + timeout: float = 0, + ) -> channels.LocalUtilsConnectResult: + result = await self._call( + "connect", + { + "endpoint": endpoint, + "headers": headers, + "exposeNetwork": exposeNetwork, + "slowMo": slowMo, + "socksProxyRedirectPortForTest": socksProxyRedirectPortForTest, + }, + timeout, + ) + return {"pipe": result["pipe"], "headers": result["headers"]} + + async def tracingStarted( + self, + tracesDir: Optional[str], + traceName: str, + live: Optional[bool], + timeout: float = 0, + ) -> channels.LocalUtilsTracingStartedResult: + result = await self._call( + "tracingStarted", + {"tracesDir": tracesDir, "traceName": traceName, "live": live}, + timeout, + ) + return {"stacksId": result["stacksId"]} + + async def addStackToTracingNoReply( + self, + callData: channels.ClientSideCallMetadata, + timeout: float = 0, + ) -> None: + await self._call("addStackToTracingNoReply", {"callData": callData}, timeout) + + async def traceDiscarded( + self, + stacksId: str, + timeout: float = 0, + ) -> None: + await self._call("traceDiscarded", {"stacksId": stacksId}, timeout) + + async def globToRegex( + self, + glob: str, + baseURL: Optional[str], + webSocketUrl: Optional[bool], + timeout: float = 0, + ) -> channels.LocalUtilsGlobToRegexResult: + result = await self._call( + "globToRegex", + {"glob": glob, "baseURL": baseURL, "webSocketUrl": webSocketUrl}, + timeout, + ) + return {"regex": result["regex"]} + + async def asLocatorDescription( + self, + selector: str, + sdkLanguage: channels.SDKLanguage, + timeout: float = 0, + ) -> channels.LocalUtilsAsLocatorDescriptionResult: + result = await self._call( + "asLocatorDescription", + {"selector": selector, "sdkLanguage": sdkLanguage}, + timeout, + ) + return {"description": result["description"]} + + +class RequestChannel(Channel): + async def response( + self, + timeout: float = 0, + ) -> channels.RequestResponseResult: + result = await self._call("response", {}, timeout) + return {"response": result.get("response")} + + async def rawRequestHeaders( + self, + timeout: float = 0, + ) -> channels.RequestRawRequestHeadersResult: + result = await self._call("rawRequestHeaders", {}, timeout) + return {"headers": result["headers"]} + + +class RouteChannel(Channel): + async def redirectNavigationRequest( + self, + url: str, + timeout: float = 0, + ) -> None: + await self._call("redirectNavigationRequest", {"url": url}, timeout) + + async def abort( + self, + errorCode: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("abort", {"errorCode": errorCode}, timeout) + + async def continue_( + self, + url: Optional[str], + method: Optional[str], + headers: Optional[Sequence[channels.NameValue]], + postData: Optional[str], + isFallback: bool, + timeout: float = 0, + ) -> None: + await self._call( + "continue", + { + "url": url, + "method": method, + "headers": headers, + "postData": postData, + "isFallback": isFallback, + }, + timeout, + ) + + async def fulfill( + self, + status: Optional[int], + headers: Optional[Sequence[channels.NameValue]], + body: Optional[str], + isBase64: Optional[bool], + fetchResponseUid: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "fulfill", + { + "status": status, + "headers": headers, + "body": body, + "isBase64": isBase64, + "fetchResponseUid": fetchResponseUid, + }, + timeout, + ) + + +class WebSocketRouteChannel(Channel): + async def connect( + self, + timeout: float = 0, + ) -> None: + await self._call("connect", {}, timeout) + + async def ensureOpened( + self, + timeout: float = 0, + ) -> None: + await self._call("ensureOpened", {}, timeout) + + async def sendToPage( + self, + message: str, + isBase64: bool, + timeout: float = 0, + ) -> None: + await self._call( + "sendToPage", {"message": message, "isBase64": isBase64}, timeout + ) + + async def sendToServer( + self, + message: str, + isBase64: bool, + timeout: float = 0, + ) -> None: + await self._call( + "sendToServer", {"message": message, "isBase64": isBase64}, timeout + ) + + async def closePage( + self, + code: Optional[int], + reason: Optional[str], + wasClean: bool, + timeout: float = 0, + ) -> None: + await self._call( + "closePage", {"code": code, "reason": reason, "wasClean": wasClean}, timeout + ) + + async def closeServer( + self, + code: Optional[int], + reason: Optional[str], + wasClean: bool, + timeout: float = 0, + ) -> None: + await self._call( + "closeServer", + {"code": code, "reason": reason, "wasClean": wasClean}, + timeout, + ) + + def onMessageFromPage( + self, handler: Callable[[channels.WebSocketRouteMessageFromPageEvent], Any] + ) -> None: + self._add_event_listener( + "messageFromPage", + handler, + lambda event: handler( + {"message": event["message"], "isBase64": event["isBase64"]} + ), + ) + + def offMessageFromPage( + self, handler: Callable[[channels.WebSocketRouteMessageFromPageEvent], Any] + ) -> None: + self._remove_event_listener("messageFromPage", handler) + + def onMessageFromServer( + self, handler: Callable[[channels.WebSocketRouteMessageFromServerEvent], Any] + ) -> None: + self._add_event_listener( + "messageFromServer", + handler, + lambda event: handler( + {"message": event["message"], "isBase64": event["isBase64"]} + ), + ) + + def offMessageFromServer( + self, handler: Callable[[channels.WebSocketRouteMessageFromServerEvent], Any] + ) -> None: + self._remove_event_listener("messageFromServer", handler) + + def onClosePage( + self, handler: Callable[[channels.WebSocketRouteClosePageEvent], Any] + ) -> None: + self._add_event_listener( + "closePage", + handler, + lambda event: handler( + { + "code": event.get("code"), + "reason": event.get("reason"), + "wasClean": event["wasClean"], + } + ), + ) + + def offClosePage( + self, handler: Callable[[channels.WebSocketRouteClosePageEvent], Any] + ) -> None: + self._remove_event_listener("closePage", handler) + + def onCloseServer( + self, handler: Callable[[channels.WebSocketRouteCloseServerEvent], Any] + ) -> None: + self._add_event_listener( + "closeServer", + handler, + lambda event: handler( + { + "code": event.get("code"), + "reason": event.get("reason"), + "wasClean": event["wasClean"], + } + ), + ) + + def offCloseServer( + self, handler: Callable[[channels.WebSocketRouteCloseServerEvent], Any] + ) -> None: + self._remove_event_listener("closeServer", handler) + + +class ResponseChannel(Channel): + async def body( + self, + timeout: float = 0, + ) -> channels.ResponseBodyResult: + result = await self._call("body", {}, timeout) + return {"binary": result["binary"]} + + async def securityDetails( + self, + timeout: float = 0, + ) -> channels.ResponseSecurityDetailsResult: + result = await self._call("securityDetails", {}, timeout) + return {"value": result.get("value")} + + async def serverAddr( + self, + timeout: float = 0, + ) -> channels.ResponseServerAddrResult: + result = await self._call("serverAddr", {}, timeout) + return {"value": result.get("value")} + + async def rawResponseHeaders( + self, + timeout: float = 0, + ) -> channels.ResponseRawResponseHeadersResult: + result = await self._call("rawResponseHeaders", {}, timeout) + return {"headers": result["headers"]} + + async def httpVersion( + self, + timeout: float = 0, + ) -> channels.ResponseHttpVersionResult: + result = await self._call("httpVersion", {}, timeout) + return {"value": result["value"]} + + async def sizes( + self, + timeout: float = 0, + ) -> channels.ResponseSizesResult: + result = await self._call("sizes", {}, timeout) + return {"sizes": result["sizes"]} + + +class WebSocketChannel(Channel): + def onOpen(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("open", handler, lambda event: handler()) + + def offOpen(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("open", handler) + + def onFrameSent( + self, handler: Callable[[channels.WebSocketFrameSentEvent], Any] + ) -> None: + self._add_event_listener( + "frameSent", + handler, + lambda event: handler({"opcode": event["opcode"], "data": event["data"]}), + ) + + def offFrameSent( + self, handler: Callable[[channels.WebSocketFrameSentEvent], Any] + ) -> None: + self._remove_event_listener("frameSent", handler) + + def onFrameReceived( + self, handler: Callable[[channels.WebSocketFrameReceivedEvent], Any] + ) -> None: + self._add_event_listener( + "frameReceived", + handler, + lambda event: handler({"opcode": event["opcode"], "data": event["data"]}), + ) + + def offFrameReceived( + self, handler: Callable[[channels.WebSocketFrameReceivedEvent], Any] + ) -> None: + self._remove_event_listener("frameReceived", handler) + + def onSocketError( + self, handler: Callable[[channels.WebSocketSocketErrorEvent], Any] + ) -> None: + self._add_event_listener( + "socketError", handler, lambda event: handler({"error": event["error"]}) + ) + + def offSocketError( + self, handler: Callable[[channels.WebSocketSocketErrorEvent], Any] + ) -> None: + self._remove_event_listener("socketError", handler) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + +class PageChannel(Channel): + async def addInitScript( + self, + source: str, + timeout: float = 0, + ) -> channels.PageAddInitScriptResult: + result = await self._call("addInitScript", {"source": source}, timeout) + return {"disposable": result["disposable"]} + + async def close( + self, + reason: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("close", {"reason": reason}, timeout) + + async def runBeforeUnload( + self, + timeout: float = 0, + ) -> None: + await self._call("runBeforeUnload", {}, timeout) + + async def clearConsoleMessages( + self, + timeout: float = 0, + ) -> None: + await self._call("clearConsoleMessages", {}, timeout) + + async def consoleMessages( + self, + filter: Optional[channels.ConsoleMessagesFilter], + timeout: float = 0, + ) -> channels.PageConsoleMessagesResult: + result = await self._call("consoleMessages", {"filter": filter}, timeout) + return {"messages": result["messages"]} + + async def emulateMedia( + self, + media: Optional[Literal["screen", "print", "no-override"]], + colorScheme: Optional[Literal["dark", "light", "no-preference", "no-override"]], + reducedMotion: Optional[Literal["reduce", "no-preference", "no-override"]], + forcedColors: Optional[Literal["active", "none", "no-override"]], + contrast: Optional[Literal["no-preference", "more", "no-override"]], + timeout: float = 0, + ) -> None: + await self._call( + "emulateMedia", + { + "media": media, + "colorScheme": colorScheme, + "reducedMotion": reducedMotion, + "forcedColors": forcedColors, + "contrast": contrast, + }, + timeout, + ) + + async def exposeBinding( + self, + name: str, + noGlobal: Optional[bool], + timeout: float = 0, + ) -> channels.PageExposeBindingResult: + result = await self._call( + "exposeBinding", {"name": name, "noGlobal": noGlobal}, timeout + ) + return {"disposable": result["disposable"]} + + async def goBack( + self, + waitUntil: Optional[channels.LifecycleEvent], + timeout: float = 0, + ) -> channels.PageGoBackResult: + result = await self._call("goBack", {"waitUntil": waitUntil}, timeout) + return {"response": result.get("response")} + + async def goForward( + self, + waitUntil: Optional[channels.LifecycleEvent], + timeout: float = 0, + ) -> channels.PageGoForwardResult: + result = await self._call("goForward", {"waitUntil": waitUntil}, timeout) + return {"response": result.get("response")} + + async def requestGC( + self, + timeout: float = 0, + ) -> None: + await self._call("requestGC", {}, timeout) + + async def registerLocatorHandler( + self, + selector: str, + noWaitAfter: Optional[bool], + timeout: float = 0, + ) -> channels.PageRegisterLocatorHandlerResult: + result = await self._call( + "registerLocatorHandler", + {"selector": selector, "noWaitAfter": noWaitAfter}, + timeout, + ) + return {"uid": result["uid"]} + + async def resolveLocatorHandlerNoReply( + self, + uid: int, + remove: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "resolveLocatorHandlerNoReply", {"uid": uid, "remove": remove}, timeout + ) + + async def unregisterLocatorHandler( + self, + uid: int, + timeout: float = 0, + ) -> None: + await self._call("unregisterLocatorHandler", {"uid": uid}, timeout) + + async def reload( + self, + waitUntil: Optional[channels.LifecycleEvent], + timeout: float = 0, + ) -> channels.PageReloadResult: + result = await self._call("reload", {"waitUntil": waitUntil}, timeout) + return {"response": result.get("response")} + + async def expectScreenshot( + self, + expected: Optional[str], + isNot: bool, + locator: Optional[channels.PageExpectScreenshotParamsLocator], + comparator: Optional[str], + maxDiffPixels: Optional[int], + maxDiffPixelRatio: Optional[float], + threshold: Optional[float], + fullPage: Optional[bool], + clip: Optional[channels.Rect], + type: Optional[Literal["png", "webp"]], + omitBackground: Optional[bool], + caret: Optional[Literal["hide", "initial"]], + animations: Optional[Literal["disabled", "allow"]], + scale: Optional[Literal["css", "device"]], + mask: Optional[Sequence[channels.PageExpectScreenshotParamsMask]], + maskColor: Optional[str], + style: Optional[str], + timeout: float = 0, + ) -> channels.PageExpectScreenshotResult: + result = await self._call( + "expectScreenshot", + { + "expected": expected, + "isNot": isNot, + "locator": locator, + "comparator": comparator, + "maxDiffPixels": maxDiffPixels, + "maxDiffPixelRatio": maxDiffPixelRatio, + "threshold": threshold, + "fullPage": fullPage, + "clip": clip, + "type": type, + "omitBackground": omitBackground, + "caret": caret, + "animations": animations, + "scale": scale, + "mask": mask, + "maskColor": maskColor, + "style": style, + }, + timeout, + ) + return {"actual": result.get("actual")} + + async def screenshot( + self, + type: Optional[Literal["png", "jpeg", "webp"]], + quality: Optional[int], + fullPage: Optional[bool], + clip: Optional[channels.Rect], + omitBackground: Optional[bool], + caret: Optional[Literal["hide", "initial"]], + animations: Optional[Literal["disabled", "allow"]], + scale: Optional[Literal["css", "device"]], + mask: Optional[Sequence[channels.PageScreenshotParamsMask]], + maskColor: Optional[str], + style: Optional[str], + timeout: float = 0, + ) -> channels.PageScreenshotResult: + result = await self._call( + "screenshot", + { + "type": type, + "quality": quality, + "fullPage": fullPage, + "clip": clip, + "omitBackground": omitBackground, + "caret": caret, + "animations": animations, + "scale": scale, + "mask": mask, + "maskColor": maskColor, + "style": style, + }, + timeout, + ) + return {"binary": result["binary"]} + + async def setExtraHTTPHeaders( + self, + headers: Sequence[channels.NameValue], + timeout: float = 0, + ) -> None: + await self._call("setExtraHTTPHeaders", {"headers": headers}, timeout) + + async def setNetworkInterceptionPatterns( + self, + patterns: Sequence[channels.PageSetNetworkInterceptionPatternsParamsPatterns], + timeout: float = 0, + ) -> None: + await self._call( + "setNetworkInterceptionPatterns", {"patterns": patterns}, timeout + ) + + async def setWebSocketInterceptionPatterns( + self, + patterns: Sequence[channels.PageSetWebSocketInterceptionPatternsParamsPatterns], + timeout: float = 0, + ) -> None: + await self._call( + "setWebSocketInterceptionPatterns", {"patterns": patterns}, timeout + ) + + async def setViewportSize( + self, + viewportSize: channels.PageSetViewportSizeParamsViewportSize, + timeout: float = 0, + ) -> None: + await self._call("setViewportSize", {"viewportSize": viewportSize}, timeout) + + async def keyboardDown( + self, + key: str, + timeout: float = 0, + ) -> None: + await self._call("keyboardDown", {"key": key}, timeout) + + async def keyboardUp( + self, + key: str, + timeout: float = 0, + ) -> None: + await self._call("keyboardUp", {"key": key}, timeout) + + async def keyboardInsertText( + self, + text: str, + timeout: float = 0, + ) -> None: + await self._call("keyboardInsertText", {"text": text}, timeout) + + async def keyboardType( + self, + text: str, + delay: Optional[float], + timeout: float = 0, + ) -> None: + await self._call("keyboardType", {"text": text, "delay": delay}, timeout) + + async def keyboardPress( + self, + key: str, + delay: Optional[float], + timeout: float = 0, + ) -> None: + await self._call("keyboardPress", {"key": key, "delay": delay}, timeout) + + async def mouseMove( + self, + x: float, + y: float, + steps: Optional[int], + timeout: float = 0, + ) -> None: + await self._call("mouseMove", {"x": x, "y": y, "steps": steps}, timeout) + + async def mouseDown( + self, + button: Optional[Literal["left", "right", "middle"]], + clickCount: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "mouseDown", {"button": button, "clickCount": clickCount}, timeout + ) + + async def mouseUp( + self, + button: Optional[Literal["left", "right", "middle"]], + clickCount: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "mouseUp", {"button": button, "clickCount": clickCount}, timeout + ) + + async def mouseClick( + self, + x: float, + y: float, + delay: Optional[float], + button: Optional[Literal["left", "right", "middle"]], + clickCount: Optional[int], + timeout: float = 0, + ) -> None: + await self._call( + "mouseClick", + { + "x": x, + "y": y, + "delay": delay, + "button": button, + "clickCount": clickCount, + }, + timeout, + ) + + async def mouseWheel( + self, + deltaX: float, + deltaY: float, + timeout: float = 0, + ) -> None: + await self._call("mouseWheel", {"deltaX": deltaX, "deltaY": deltaY}, timeout) + + async def touchscreenTap( + self, + x: float, + y: float, + timeout: float = 0, + ) -> None: + await self._call("touchscreenTap", {"x": x, "y": y}, timeout) + + async def clearPageErrors( + self, + timeout: float = 0, + ) -> None: + await self._call("clearPageErrors", {}, timeout) + + async def pageErrors( + self, + filter: Optional[channels.ConsoleMessagesFilter], + timeout: float = 0, + ) -> channels.PagePageErrorsResult: + result = await self._call("pageErrors", {"filter": filter}, timeout) + return {"errors": result["errors"]} + + async def pdf( + self, + scale: Optional[float], + displayHeaderFooter: Optional[bool], + headerTemplate: Optional[str], + footerTemplate: Optional[str], + printBackground: Optional[bool], + landscape: Optional[bool], + pageRanges: Optional[str], + format: Optional[str], + width: Optional[str], + height: Optional[str], + preferCSSPageSize: Optional[bool], + margin: Optional[channels.PagePdfParamsMargin], + tagged: Optional[bool], + outline: Optional[bool], + timeout: float = 0, + ) -> channels.PagePdfResult: + result = await self._call( + "pdf", + { + "scale": scale, + "displayHeaderFooter": displayHeaderFooter, + "headerTemplate": headerTemplate, + "footerTemplate": footerTemplate, + "printBackground": printBackground, + "landscape": landscape, + "pageRanges": pageRanges, + "format": format, + "width": width, + "height": height, + "preferCSSPageSize": preferCSSPageSize, + "margin": margin, + "tagged": tagged, + "outline": outline, + }, + timeout, + ) + return {"pdf": result["pdf"]} + + async def requests( + self, + timeout: float = 0, + ) -> channels.PageRequestsResult: + result = await self._call("requests", {}, timeout) + return {"requests": result["requests"]} + + async def startJSCoverage( + self, + resetOnNavigation: Optional[bool], + reportAnonymousScripts: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "startJSCoverage", + { + "resetOnNavigation": resetOnNavigation, + "reportAnonymousScripts": reportAnonymousScripts, + }, + timeout, + ) + + async def stopJSCoverage( + self, + timeout: float = 0, + ) -> channels.PageStopJSCoverageResult: + result = await self._call("stopJSCoverage", {}, timeout) + return {"entries": result["entries"]} + + async def startCSSCoverage( + self, + resetOnNavigation: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "startCSSCoverage", {"resetOnNavigation": resetOnNavigation}, timeout + ) + + async def stopCSSCoverage( + self, + timeout: float = 0, + ) -> channels.PageStopCSSCoverageResult: + result = await self._call("stopCSSCoverage", {}, timeout) + return {"entries": result["entries"]} + + async def bringToFront( + self, + timeout: float = 0, + ) -> None: + await self._call("bringToFront", {}, timeout) + + async def pickLocator( + self, + timeout: float = 0, + ) -> channels.PagePickLocatorResult: + result = await self._call("pickLocator", {}, timeout) + return {"selector": result["selector"]} + + async def cancelPickLocator( + self, + timeout: float = 0, + ) -> None: + await self._call("cancelPickLocator", {}, timeout) + + async def hideHighlight( + self, + timeout: float = 0, + ) -> None: + await self._call("hideHighlight", {}, timeout) + + async def screencastShowOverlay( + self, + html: str, + duration: Optional[float], + timeout: float = 0, + ) -> channels.PageScreencastShowOverlayResult: + result = await self._call( + "screencastShowOverlay", {"html": html, "duration": duration}, timeout + ) + return {"id": result["id"]} + + async def screencastRemoveOverlay( + self, + id: str, + timeout: float = 0, + ) -> None: + await self._call("screencastRemoveOverlay", {"id": id}, timeout) + + async def screencastChapter( + self, + title: str, + description: Optional[str], + duration: Optional[float], + timeout: float = 0, + ) -> None: + await self._call( + "screencastChapter", + {"title": title, "description": description, "duration": duration}, + timeout, + ) + + async def screencastSetOverlayVisible( + self, + visible: bool, + timeout: float = 0, + ) -> None: + await self._call("screencastSetOverlayVisible", {"visible": visible}, timeout) + + async def screencastShowActions( + self, + duration: Optional[float], + position: Optional[ + Literal[ + "top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right" + ] + ], + fontSize: Optional[int], + cursor: Optional[Literal["none", "pointer"]], + style: Optional[channels.PageScreencastShowActionsParamsStyle], + timeout: float = 0, + ) -> None: + await self._call( + "screencastShowActions", + { + "duration": duration, + "position": position, + "fontSize": fontSize, + "cursor": cursor, + "style": style, + }, + timeout, + ) + + async def screencastHideActions( + self, + timeout: float = 0, + ) -> None: + await self._call("screencastHideActions", {}, timeout) + + async def screencastStart( + self, + size: Optional[channels.PageScreencastStartParamsSize], + quality: Optional[int], + fps: Optional[int], + sendFrames: Optional[bool], + record: Optional[bool], + timeout: float = 0, + ) -> channels.PageScreencastStartResult: + result = await self._call( + "screencastStart", + { + "size": size, + "quality": quality, + "fps": fps, + "sendFrames": sendFrames, + "record": record, + }, + timeout, + ) + return {"artifact": result.get("artifact")} + + async def screencastFrameAck( + self, + frameId: int, + timeout: float = 0, + ) -> None: + await self._call("screencastFrameAck", {"frameId": frameId}, timeout) + + async def screencastStop( + self, + timeout: float = 0, + ) -> None: + await self._call("screencastStop", {}, timeout) + + async def updateSubscription( + self, + event: Literal[ + "console", + "dialog", + "dialogClosed", + "fileChooser", + "request", + "response", + "requestFinished", + "requestFailed", + ], + enabled: bool, + timeout: float = 0, + ) -> None: + await self._call( + "updateSubscription", {"event": event, "enabled": enabled}, timeout + ) + + async def setDockTile( + self, + image: str, + timeout: float = 0, + ) -> None: + await self._call("setDockTile", {"image": image}, timeout) + + async def webStorageItems( + self, + kind: Literal["local", "session"], + timeout: float = 0, + ) -> channels.PageWebStorageItemsResult: + result = await self._call("webStorageItems", {"kind": kind}, timeout) + return {"items": result["items"]} + + async def webStorageGetItem( + self, + kind: Literal["local", "session"], + name: str, + timeout: float = 0, + ) -> channels.PageWebStorageGetItemResult: + result = await self._call( + "webStorageGetItem", {"kind": kind, "name": name}, timeout + ) + return {"value": result.get("value")} + + async def webStorageSetItem( + self, + kind: Literal["local", "session"], + name: str, + value: str, + timeout: float = 0, + ) -> None: + await self._call( + "webStorageSetItem", {"kind": kind, "name": name, "value": value}, timeout + ) + + async def webStorageRemoveItem( + self, + kind: Literal["local", "session"], + name: str, + timeout: float = 0, + ) -> None: + await self._call("webStorageRemoveItem", {"kind": kind, "name": name}, timeout) + + async def webStorageClear( + self, + kind: Literal["local", "session"], + timeout: float = 0, + ) -> None: + await self._call("webStorageClear", {"kind": kind}, timeout) + + def onBindingCall( + self, handler: Callable[[channels.PageBindingCallEvent], Any] + ) -> None: + self._add_event_listener( + "bindingCall", handler, lambda event: handler({"binding": event["binding"]}) + ) + + def offBindingCall( + self, handler: Callable[[channels.PageBindingCallEvent], Any] + ) -> None: + self._remove_event_listener("bindingCall", handler) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + def onCrash(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("crash", handler, lambda event: handler()) + + def offCrash(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("crash", handler) + + def onDownload(self, handler: Callable[[channels.PageDownloadEvent], Any]) -> None: + self._add_event_listener( + "download", + handler, + lambda event: handler( + { + "url": event["url"], + "suggestedFilename": event["suggestedFilename"], + "artifact": event["artifact"], + } + ), + ) + + def offDownload(self, handler: Callable[[channels.PageDownloadEvent], Any]) -> None: + self._remove_event_listener("download", handler) + + def onViewportSizeChanged( + self, handler: Callable[[channels.PageViewportSizeChangedEvent], Any] + ) -> None: + self._add_event_listener( + "viewportSizeChanged", + handler, + lambda event: handler({"viewportSize": event.get("viewportSize")}), + ) + + def offViewportSizeChanged( + self, handler: Callable[[channels.PageViewportSizeChangedEvent], Any] + ) -> None: + self._remove_event_listener("viewportSizeChanged", handler) + + def onFileChooser( + self, handler: Callable[[channels.PageFileChooserEvent], Any] + ) -> None: + self._add_event_listener( + "fileChooser", + handler, + lambda event: handler( + {"element": event["element"], "isMultiple": event["isMultiple"]} + ), + ) + + def offFileChooser( + self, handler: Callable[[channels.PageFileChooserEvent], Any] + ) -> None: + self._remove_event_listener("fileChooser", handler) + + def onFrameAttached( + self, handler: Callable[[channels.PageFrameAttachedEvent], Any] + ) -> None: + self._add_event_listener( + "frameAttached", handler, lambda event: handler({"frame": event["frame"]}) + ) + + def offFrameAttached( + self, handler: Callable[[channels.PageFrameAttachedEvent], Any] + ) -> None: + self._remove_event_listener("frameAttached", handler) + + def onFrameDetached( + self, handler: Callable[[channels.PageFrameDetachedEvent], Any] + ) -> None: + self._add_event_listener( + "frameDetached", handler, lambda event: handler({"frame": event["frame"]}) + ) + + def offFrameDetached( + self, handler: Callable[[channels.PageFrameDetachedEvent], Any] + ) -> None: + self._remove_event_listener("frameDetached", handler) + + def onLocatorHandlerTriggered( + self, handler: Callable[[channels.PageLocatorHandlerTriggeredEvent], Any] + ) -> None: + self._add_event_listener( + "locatorHandlerTriggered", + handler, + lambda event: handler({"uid": event["uid"]}), + ) + + def offLocatorHandlerTriggered( + self, handler: Callable[[channels.PageLocatorHandlerTriggeredEvent], Any] + ) -> None: + self._remove_event_listener("locatorHandlerTriggered", handler) + + def onRoute(self, handler: Callable[[channels.PageRouteEvent], Any]) -> None: + self._add_event_listener( + "route", handler, lambda event: handler({"route": event["route"]}) + ) + + def offRoute(self, handler: Callable[[channels.PageRouteEvent], Any]) -> None: + self._remove_event_listener("route", handler) + + def onScreencastFrame( + self, handler: Callable[[channels.PageScreencastFrameEvent], Any] + ) -> None: + self._add_event_listener( + "screencastFrame", + handler, + lambda event: handler( + { + "frameId": event["frameId"], + "data": event["data"], + "timestamp": event["timestamp"], + "viewportWidth": event["viewportWidth"], + "viewportHeight": event["viewportHeight"], + } + ), + ) + + def offScreencastFrame( + self, handler: Callable[[channels.PageScreencastFrameEvent], Any] + ) -> None: + self._remove_event_listener("screencastFrame", handler) + + def onWebSocketRoute( + self, handler: Callable[[channels.PageWebSocketRouteEvent], Any] + ) -> None: + self._add_event_listener( + "webSocketRoute", + handler, + lambda event: handler({"webSocketRoute": event["webSocketRoute"]}), + ) + + def offWebSocketRoute( + self, handler: Callable[[channels.PageWebSocketRouteEvent], Any] + ) -> None: + self._remove_event_listener("webSocketRoute", handler) + + def onWebSocket( + self, handler: Callable[[channels.PageWebSocketEvent], Any] + ) -> None: + self._add_event_listener( + "webSocket", + handler, + lambda event: handler({"webSocket": event["webSocket"]}), + ) + + def offWebSocket( + self, handler: Callable[[channels.PageWebSocketEvent], Any] + ) -> None: + self._remove_event_listener("webSocket", handler) + + def onWorker(self, handler: Callable[[channels.PageWorkerEvent], Any]) -> None: + self._add_event_listener( + "worker", handler, lambda event: handler({"worker": event["worker"]}) + ) + + def offWorker(self, handler: Callable[[channels.PageWorkerEvent], Any]) -> None: + self._remove_event_listener("worker", handler) + + +class RootChannel(Channel): + async def initialize( + self, + sdkLanguage: channels.SDKLanguage, + timeout: float = 0, + ) -> channels.RootInitializeResult: + result = await self._call("initialize", {"sdkLanguage": sdkLanguage}, timeout) + return {"playwright": result["playwright"]} + + +class PlaywrightChannel(Channel): + async def newRequest( + self, + baseURL: Optional[str], + userAgent: Optional[str], + ignoreHTTPSErrors: Optional[bool], + extraHTTPHeaders: Optional[Sequence[channels.NameValue]], + failOnStatusCode: Optional[bool], + clientCertificates: Optional[ + Sequence[channels.PlaywrightNewRequestParamsClientCertificates] + ], + maxRedirects: Optional[int], + httpCredentials: Optional[Sequence[channels.HttpCredentials]], + proxy: Optional[channels.PlaywrightNewRequestParamsProxy], + storageState: Optional[channels.PlaywrightNewRequestParamsStorageState], + tracesDir: Optional[str], + timeout: float = 0, + ) -> channels.PlaywrightNewRequestResult: + result = await self._call( + "newRequest", + { + "baseURL": baseURL, + "userAgent": userAgent, + "ignoreHTTPSErrors": ignoreHTTPSErrors, + "extraHTTPHeaders": extraHTTPHeaders, + "failOnStatusCode": failOnStatusCode, + "clientCertificates": clientCertificates, + "maxRedirects": maxRedirects, + "httpCredentials": httpCredentials, + "proxy": proxy, + "storageState": storageState, + "tracesDir": tracesDir, + }, + timeout, + ) + return {"request": result["request"]} + + +class DebugControllerChannel(Channel): + async def initialize( + self, + codegenId: str, + sdkLanguage: channels.SDKLanguage, + timeout: float = 0, + ) -> None: + await self._call( + "initialize", {"codegenId": codegenId, "sdkLanguage": sdkLanguage}, timeout + ) + + async def setReportStateChanged( + self, + enabled: bool, + timeout: float = 0, + ) -> None: + await self._call("setReportStateChanged", {"enabled": enabled}, timeout) + + async def setRecorderMode( + self, + mode: Literal["inspecting", "recording", "none"], + testIdAttributeName: Optional[str], + generateAutoExpect: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "setRecorderMode", + { + "mode": mode, + "testIdAttributeName": testIdAttributeName, + "generateAutoExpect": generateAutoExpect, + }, + timeout, + ) + + async def highlight( + self, + selector: Optional[str], + ariaTemplate: Optional[str], + timeout: float = 0, + ) -> None: + await self._call( + "highlight", {"selector": selector, "ariaTemplate": ariaTemplate}, timeout + ) + + async def hideHighlight( + self, + timeout: float = 0, + ) -> None: + await self._call("hideHighlight", {}, timeout) + + async def resume( + self, + timeout: float = 0, + ) -> None: + await self._call("resume", {}, timeout) + + async def kill( + self, + timeout: float = 0, + ) -> None: + await self._call("kill", {}, timeout) + + def onInspectRequested( + self, handler: Callable[[channels.DebugControllerInspectRequestedEvent], Any] + ) -> None: + self._add_event_listener( + "inspectRequested", + handler, + lambda event: handler( + { + "selector": event["selector"], + "locator": event["locator"], + "ariaSnapshot": event["ariaSnapshot"], + } + ), + ) + + def offInspectRequested( + self, handler: Callable[[channels.DebugControllerInspectRequestedEvent], Any] + ) -> None: + self._remove_event_listener("inspectRequested", handler) + + def onSetModeRequested( + self, handler: Callable[[channels.DebugControllerSetModeRequestedEvent], Any] + ) -> None: + self._add_event_listener( + "setModeRequested", handler, lambda event: handler({"mode": event["mode"]}) + ) + + def offSetModeRequested( + self, handler: Callable[[channels.DebugControllerSetModeRequestedEvent], Any] + ) -> None: + self._remove_event_listener("setModeRequested", handler) + + def onStateChanged( + self, handler: Callable[[channels.DebugControllerStateChangedEvent], Any] + ) -> None: + self._add_event_listener( + "stateChanged", + handler, + lambda event: handler({"pageCount": event["pageCount"]}), + ) + + def offStateChanged( + self, handler: Callable[[channels.DebugControllerStateChangedEvent], Any] + ) -> None: + self._remove_event_listener("stateChanged", handler) + + def onSourceChanged( + self, handler: Callable[[channels.DebugControllerSourceChangedEvent], Any] + ) -> None: + self._add_event_listener( + "sourceChanged", + handler, + lambda event: handler( + { + "text": event["text"], + "header": event.get("header"), + "footer": event.get("footer"), + "actions": event.get("actions"), + } + ), + ) + + def offSourceChanged( + self, handler: Callable[[channels.DebugControllerSourceChangedEvent], Any] + ) -> None: + self._remove_event_listener("sourceChanged", handler) + + def onPaused( + self, handler: Callable[[channels.DebugControllerPausedEvent], Any] + ) -> None: + self._add_event_listener( + "paused", handler, lambda event: handler({"paused": event["paused"]}) + ) + + def offPaused( + self, handler: Callable[[channels.DebugControllerPausedEvent], Any] + ) -> None: + self._remove_event_listener("paused", handler) + + +class SocksSupportChannel(Channel): + async def socksConnected( + self, + uid: str, + host: str, + port: int, + timeout: float = 0, + ) -> None: + await self._call( + "socksConnected", {"uid": uid, "host": host, "port": port}, timeout + ) + + async def socksFailed( + self, + uid: str, + errorCode: str, + timeout: float = 0, + ) -> None: + await self._call("socksFailed", {"uid": uid, "errorCode": errorCode}, timeout) + + async def socksData( + self, + uid: str, + data: str, + timeout: float = 0, + ) -> None: + await self._call("socksData", {"uid": uid, "data": data}, timeout) + + async def socksError( + self, + uid: str, + error: str, + timeout: float = 0, + ) -> None: + await self._call("socksError", {"uid": uid, "error": error}, timeout) + + async def socksEnd( + self, + uid: str, + timeout: float = 0, + ) -> None: + await self._call("socksEnd", {"uid": uid}, timeout) + + def onSocksRequested( + self, handler: Callable[[channels.SocksSupportSocksRequestedEvent], Any] + ) -> None: + self._add_event_listener( + "socksRequested", + handler, + lambda event: handler( + {"uid": event["uid"], "host": event["host"], "port": event["port"]} + ), + ) + + def offSocksRequested( + self, handler: Callable[[channels.SocksSupportSocksRequestedEvent], Any] + ) -> None: + self._remove_event_listener("socksRequested", handler) + + def onSocksData( + self, handler: Callable[[channels.SocksSupportSocksDataEvent], Any] + ) -> None: + self._add_event_listener( + "socksData", + handler, + lambda event: handler({"uid": event["uid"], "data": event["data"]}), + ) + + def offSocksData( + self, handler: Callable[[channels.SocksSupportSocksDataEvent], Any] + ) -> None: + self._remove_event_listener("socksData", handler) + + def onSocksClosed( + self, handler: Callable[[channels.SocksSupportSocksClosedEvent], Any] + ) -> None: + self._add_event_listener( + "socksClosed", handler, lambda event: handler({"uid": event["uid"]}) + ) + + def offSocksClosed( + self, handler: Callable[[channels.SocksSupportSocksClosedEvent], Any] + ) -> None: + self._remove_event_listener("socksClosed", handler) + + +class JsonPipeChannel(Channel): + async def send( + self, + message: Any, + timeout: float = 0, + ) -> None: + await self._call("send", {"message": message}, timeout) + + async def close( + self, + timeout: float = 0, + ) -> None: + await self._call("close", {}, timeout) + + def onMessage( + self, handler: Callable[[channels.JsonPipeMessageEvent], Any] + ) -> None: + self._add_event_listener( + "message", handler, lambda event: handler({"message": event["message"]}) + ) + + def offMessage( + self, handler: Callable[[channels.JsonPipeMessageEvent], Any] + ) -> None: + self._remove_event_listener("message", handler) + + def onClosed(self, handler: Callable[[channels.JsonPipeClosedEvent], Any]) -> None: + self._add_event_listener( + "closed", handler, lambda event: handler({"reason": event.get("reason")}) + ) + + def offClosed(self, handler: Callable[[channels.JsonPipeClosedEvent], Any]) -> None: + self._remove_event_listener("closed", handler) + + +class CDPSessionChannel(Channel): + async def send( + self, + method: str, + params: Optional[Any], + timeout: float = 0, + ) -> channels.CDPSessionSendResult: + result = await self._call("send", {"method": method, "params": params}, timeout) + return {"result": result["result"]} + + async def detach( + self, + timeout: float = 0, + ) -> None: + await self._call("detach", {}, timeout) + + def onEvent(self, handler: Callable[[channels.CDPSessionEventEvent], Any]) -> None: + self._add_event_listener( + "event", + handler, + lambda event: handler( + {"method": event["method"], "params": event.get("params")} + ), + ) + + def offEvent(self, handler: Callable[[channels.CDPSessionEventEvent], Any]) -> None: + self._remove_event_listener("event", handler) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + +class BindingCallChannel(Channel): + async def reject( + self, + error: channels.SerializedError, + timeout: float = 0, + ) -> None: + await self._call("reject", {"error": error}, timeout) + + async def resolve( + self, + result: channels.SerializedArgument, + timeout: float = 0, + ) -> None: + await self._call("resolve", {"result": result}, timeout) + + +class DebuggerChannel(Channel): + async def requestPause( + self, + timeout: float = 0, + ) -> None: + await self._call("requestPause", {}, timeout) + + async def resume( + self, + timeout: float = 0, + ) -> None: + await self._call("resume", {}, timeout) + + async def next( + self, + timeout: float = 0, + ) -> None: + await self._call("next", {}, timeout) + + async def runTo( + self, + location: channels.DebuggerRunToParamsLocation, + timeout: float = 0, + ) -> None: + await self._call("runTo", {"location": location}, timeout) + + async def enable( + self, + timeout: float = 0, + ) -> None: + await self._call("enable", {}, timeout) + + def onPausedStateChanged( + self, handler: Callable[[channels.DebuggerPausedStateChangedEvent], Any] + ) -> None: + self._add_event_listener( + "pausedStateChanged", + handler, + lambda event: handler({"pausedDetails": event.get("pausedDetails")}), + ) + + def offPausedStateChanged( + self, handler: Callable[[channels.DebuggerPausedStateChangedEvent], Any] + ) -> None: + self._remove_event_listener("pausedStateChanged", handler) + + def onApiCallsUpdated( + self, handler: Callable[[channels.DebuggerApiCallsUpdatedEvent], Any] + ) -> None: + self._add_event_listener( + "apiCallsUpdated", + handler, + lambda event: handler({"apiCalls": event["apiCalls"]}), + ) + + def offApiCallsUpdated( + self, handler: Callable[[channels.DebuggerApiCallsUpdatedEvent], Any] + ) -> None: + self._remove_event_listener("apiCallsUpdated", handler) + + +class DialogChannel(Channel): + async def accept( + self, + promptText: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("accept", {"promptText": promptText}, timeout) + + async def dismiss( + self, + timeout: float = 0, + ) -> None: + await self._call("dismiss", {}, timeout) + + +class TracingChannel(Channel): + async def tracingStart( + self, + name: Optional[str], + snapshotDom: Optional[bool], + snapshotAria: Optional[bool], + snapshotScreen: Optional[bool], + screencast: Optional[bool], + coverage: Optional[bool], + live: Optional[bool], + timeout: float = 0, + ) -> None: + await self._call( + "tracingStart", + { + "name": name, + "snapshotDom": snapshotDom, + "snapshotAria": snapshotAria, + "snapshotScreen": snapshotScreen, + "screencast": screencast, + "coverage": coverage, + "live": live, + }, + timeout, + ) + + async def tracingStartChunk( + self, + name: Optional[str], + title: Optional[str], + timeout: float = 0, + ) -> channels.TracingTracingStartChunkResult: + result = await self._call( + "tracingStartChunk", {"name": name, "title": title}, timeout + ) + return {"traceName": result["traceName"]} + + async def tracingGroup( + self, + name: str, + location: Optional[channels.TracingTracingGroupParamsLocation], + timeout: float = 0, + ) -> None: + await self._call("tracingGroup", {"name": name, "location": location}, timeout) + + async def tracingGroupEnd( + self, + timeout: float = 0, + ) -> None: + await self._call("tracingGroupEnd", {}, timeout) + + async def tracingStopChunk( + self, + mode: Literal["archive", "discard", "entries"], + timeout: float = 0, + ) -> channels.TracingTracingStopChunkResult: + result = await self._call("tracingStopChunk", {"mode": mode}, timeout) + return {"artifact": result.get("artifact"), "entries": result.get("entries")} + + async def tracingStop( + self, + timeout: float = 0, + ) -> None: + await self._call("tracingStop", {}, timeout) + + async def harStart( + self, + page: Optional[PageChannel], + options: channels.RecordHarOptions, + timeout: float = 0, + ) -> channels.TracingHarStartResult: + result = await self._call( + "harStart", {"page": page, "options": options}, timeout + ) + return {"harId": result["harId"]} + + async def harExport( + self, + harId: Optional[str], + mode: Literal["archive", "entries"], + timeout: float = 0, + ) -> channels.TracingHarExportResult: + result = await self._call("harExport", {"harId": harId, "mode": mode}, timeout) + return {"artifact": result.get("artifact"), "entries": result.get("entries")} + + +class WorkerChannel(Channel): + async def disconnect( + self, + reason: Optional[str], + timeout: float = 0, + ) -> None: + await self._call("disconnect", {"reason": reason}, timeout) + + async def evaluateExpression( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.WorkerEvaluateExpressionResult: + result = await self._call( + "evaluateExpression", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"value": result["value"]} + + async def evaluateExpressionHandle( + self, + expression: str, + isFunction: Optional[bool], + arg: channels.SerializedArgument, + timeout: float = 0, + ) -> channels.WorkerEvaluateExpressionHandleResult: + result = await self._call( + "evaluateExpressionHandle", + {"expression": expression, "isFunction": isFunction, "arg": arg}, + timeout, + ) + return {"handle": result["handle"]} + + async def updateSubscription( + self, + event: Literal["console"], + enabled: bool, + timeout: float = 0, + ) -> None: + await self._call( + "updateSubscription", {"event": event, "enabled": enabled}, timeout + ) + + def onConsole(self, handler: Callable[[channels.WorkerConsoleEvent], Any]) -> None: + self._add_event_listener( + "console", + handler, + lambda event: handler( + { + "type": event["type"], + "text": event["text"], + "args": event["args"], + "location": event["location"], + "timestamp": event["timestamp"], + } + ), + ) + + def offConsole(self, handler: Callable[[channels.WorkerConsoleEvent], Any]) -> None: + self._remove_event_listener("console", handler) + + def onClose(self, handler: Callable[[], Any]) -> None: + self._add_event_listener("close", handler, lambda event: handler()) + + def offClose(self, handler: Callable[[], Any]) -> None: + self._remove_event_listener("close", handler) + + +# The channel class for each protocol type name. +CHANNEL_CLASSES: Dict[str, Type[Channel]] = { + "Android": AndroidChannel, + "AndroidSocket": AndroidSocketChannel, + "AndroidDevice": AndroidDeviceChannel, + "APIRequestContext": APIRequestContextChannel, + "Artifact": ArtifactChannel, + "Stream": StreamChannel, + "WritableStream": WritableStreamChannel, + "Browser": BrowserChannel, + "BrowserContext": BrowserContextChannel, + "BrowserType": BrowserTypeChannel, + "Disposable": DisposableChannel, + "Electron": ElectronChannel, + "ElectronApplication": ElectronApplicationChannel, + "Frame": FrameChannel, + "JSHandle": JSHandleChannel, + "ElementHandle": ElementHandleChannel, + "LocalUtils": LocalUtilsChannel, + "Request": RequestChannel, + "Route": RouteChannel, + "WebSocketRoute": WebSocketRouteChannel, + "Response": ResponseChannel, + "WebSocket": WebSocketChannel, + "Page": PageChannel, + "Root": RootChannel, + "Playwright": PlaywrightChannel, + "DebugController": DebugControllerChannel, + "SocksSupport": SocksSupportChannel, + "JsonPipe": JsonPipeChannel, + "CDPSession": CDPSessionChannel, + "BindingCall": BindingCallChannel, + "Debugger": DebuggerChannel, + "Dialog": DialogChannel, + "Tracing": TracingChannel, + "Worker": WorkerChannel, +} diff --git a/playwright/_impl/_protocol_types.py b/playwright/_impl/_protocol_types.py new file mode 100644 index 000000000..16fcc36d3 --- /dev/null +++ b/playwright/_impl/_protocol_types.py @@ -0,0 +1,2152 @@ +# Copyright (c) Microsoft Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Types of the protocol messages, generated from packages/protocol/spec. + +The names follow upstream's utils/generate_channels.js: Initializer, +Result and Event. The parameters of each +command are the arguments of its method in _protocol_channels.py. An object that +the spec writes inline is named after its owner and field, for example +PageInitializerViewportSize or BrowserNewContextParamsProxy. A reference to +another protocol object has the type Channel. + +The top-level fields of a result or an event are all present; a field that the +driver left out is None. In the data that the client sends, an optional field of +a nested object also accepts None, except in objects inside arrays, and an array +accepts any sequence. The connection drops None values from objects, but not from +objects inside arrays, before it sends a message. + +Generated by scripts/codegen/generate_protocol.py. Do not edit. +""" + +from __future__ import annotations + +from typing import ( + TYPE_CHECKING, + Any, + List, + Literal, + Optional, + Sequence, + TypedDict, +) + +if TYPE_CHECKING: + from typing_extensions import NotRequired + + from playwright._impl._connection import Channel + from playwright._impl._protocol_channels import ( + AndroidChannel, + AndroidDeviceChannel, + AndroidSocketChannel, + APIRequestContextChannel, + ArtifactChannel, + BindingCallChannel, + BrowserChannel, + BrowserContextChannel, + BrowserTypeChannel, + CDPSessionChannel, + DebuggerChannel, + DialogChannel, + DisposableChannel, + ElectronApplicationChannel, + ElectronChannel, + ElementHandleChannel, + FrameChannel, + JSHandleChannel, + JsonPipeChannel, + LocalUtilsChannel, + PageChannel, + PlaywrightChannel, + RequestChannel, + ResponseChannel, + RouteChannel, + SocksSupportChannel, + StreamChannel, + TracingChannel, + WebSocketChannel, + WebSocketRouteChannel, + WorkerChannel, + WritableStreamChannel, + ) + +SDKLanguage = Literal["javascript", "python", "java", "csharp"] +LifecycleEvent = Literal["load", "domcontentloaded", "networkidle", "commit"] +ConsoleMessagesFilter = Literal["all", "since-navigation"] + + +class AndroidDevicesResult(TypedDict): + devices: List[AndroidDeviceChannel] + + +class AndroidSocketDataEvent(TypedDict): + data: str + + +class AndroidDeviceInitializer(TypedDict): + model: str + serial: str + + +class AndroidDeviceInfoResult(TypedDict): + info: AndroidElementInfo + + +class AndroidDeviceScreenshotResult(TypedDict): + binary: str + + +class AndroidDeviceLaunchBrowserParamsViewport(TypedDict): + width: int + height: int + + +class AndroidDeviceLaunchBrowserParamsScreen(TypedDict): + width: int + height: int + + +class AndroidDeviceLaunchBrowserParamsClientCertificates(TypedDict): + origin: str + cert: NotRequired[str] + key: NotRequired[str] + passphrase: NotRequired[str] + pfx: NotRequired[str] + + +class AndroidDeviceLaunchBrowserParamsGeolocation(TypedDict): + longitude: float + latitude: float + accuracy: NotRequired[Optional[float]] + + +class AndroidDeviceLaunchBrowserParamsRecordVideoSize(TypedDict): + width: int + height: int + + +class AndroidDeviceLaunchBrowserParamsRecordVideoShowActionsStyle(TypedDict): + point: NotRequired[Optional[str]] + highlight: NotRequired[Optional[str]] + title: NotRequired[Optional[str]] + + +class AndroidDeviceLaunchBrowserParamsRecordVideoShowActions(TypedDict): + duration: NotRequired[Optional[float]] + position: NotRequired[ + Optional[ + Literal[ + "top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right" + ] + ] + ] + fontSize: NotRequired[Optional[int]] + cursor: NotRequired[Optional[Literal["none", "pointer"]]] + style: NotRequired[ + Optional[AndroidDeviceLaunchBrowserParamsRecordVideoShowActionsStyle] + ] + + +class AndroidDeviceLaunchBrowserParamsRecordVideo(TypedDict): + dir: NotRequired[Optional[str]] + size: NotRequired[Optional[AndroidDeviceLaunchBrowserParamsRecordVideoSize]] + fps: NotRequired[Optional[int]] + showActions: NotRequired[ + Optional[AndroidDeviceLaunchBrowserParamsRecordVideoShowActions] + ] + + +class AndroidDeviceLaunchBrowserParamsProxy(TypedDict): + server: str + bypass: NotRequired[Optional[str]] + username: NotRequired[Optional[str]] + password: NotRequired[Optional[str]] + + +class AndroidDeviceLaunchBrowserResult(TypedDict): + context: BrowserContextChannel + + +class AndroidDeviceOpenResult(TypedDict): + socket: AndroidSocketChannel + + +class AndroidDeviceShellResult(TypedDict): + result: str + + +class AndroidDeviceConnectToWebViewResult(TypedDict): + context: BrowserContextChannel + + +class AndroidDeviceWebViewAddedEvent(TypedDict): + webView: AndroidWebView + + +class AndroidDeviceWebViewRemovedEvent(TypedDict): + socketName: str + + +class AndroidWebView(TypedDict): + pid: int + pkg: str + socketName: str + + +class AndroidSelectorHasChild(TypedDict): + androidSelector: AndroidSelector + + +class AndroidSelectorHasDescendant(TypedDict): + androidSelector: AndroidSelector + maxDepth: NotRequired[int] + + +class AndroidSelector(TypedDict): + checkable: NotRequired[bool] + checked: NotRequired[bool] + clazz: NotRequired[str] + clickable: NotRequired[bool] + depth: NotRequired[int] + desc: NotRequired[str] + enabled: NotRequired[bool] + focusable: NotRequired[bool] + focused: NotRequired[bool] + hasChild: NotRequired[AndroidSelectorHasChild] + hasDescendant: NotRequired[AndroidSelectorHasDescendant] + longClickable: NotRequired[bool] + pkg: NotRequired[str] + res: NotRequired[str] + scrollable: NotRequired[bool] + selected: NotRequired[bool] + text: NotRequired[str] + + +class AndroidElementInfo(TypedDict): + children: NotRequired[List[AndroidElementInfo]] + clazz: str + desc: str + res: str + pkg: str + text: str + bounds: Rect + checkable: bool + checked: bool + clickable: bool + enabled: bool + focusable: bool + focused: bool + longClickable: bool + scrollable: bool + selected: bool + + +class APIRequestContextInitializer(TypedDict): + tracing: TracingChannel + + +class APIRequestContextFetchResult(TypedDict): + response: APIResponse + + +class APIRequestContextFetchResponseBodyResult(TypedDict): + binary: Optional[str] + + +class APIRequestContextFetchLogResult(TypedDict): + log: List[str] + + +class APIRequestContextCookiesResult(TypedDict): + cookies: List[NetworkCookie] + + +class APIRequestContextStorageStateResult(TypedDict): + cookies: List[NetworkCookie] + origins: List[OriginStorage] + + +class APIResponse(TypedDict): + fetchUid: str + url: str + status: int + statusText: str + headers: List[NameValue] + securityDetails: NotRequired[SecurityDetails] + serverAddr: NotRequired[RemoteAddr] + timing: NotRequired[ResourceTiming] + responseEndTiming: NotRequired[float] + + +class ArtifactInitializer(TypedDict): + absolutePath: str + + +class ArtifactPathAfterFinishedResult(TypedDict): + value: str + + +class ArtifactSaveAsStreamResult(TypedDict): + stream: StreamChannel + + +class ArtifactFailureResult(TypedDict): + error: Optional[str] + + +class ArtifactStreamResult(TypedDict): + stream: StreamChannel + + +class StreamReadResult(TypedDict): + binary: str + + +class BrowserInitializer(TypedDict): + version: str + name: str + browserName: Literal["chromium", "firefox", "webkit"] + + +class BrowserStartServerResult(TypedDict): + endpoint: str + + +class BrowserDefaultUserAgentForTestResult(TypedDict): + userAgent: str + + +class BrowserNewContextParamsViewport(TypedDict): + width: int + height: int + + +class BrowserNewContextParamsScreen(TypedDict): + width: int + height: int + + +class BrowserNewContextParamsClientCertificates(TypedDict): + origin: str + cert: NotRequired[str] + key: NotRequired[str] + passphrase: NotRequired[str] + pfx: NotRequired[str] + + +class BrowserNewContextParamsGeolocation(TypedDict): + longitude: float + latitude: float + accuracy: NotRequired[Optional[float]] + + +class BrowserNewContextParamsRecordVideoSize(TypedDict): + width: int + height: int + + +class BrowserNewContextParamsRecordVideoShowActionsStyle(TypedDict): + point: NotRequired[Optional[str]] + highlight: NotRequired[Optional[str]] + title: NotRequired[Optional[str]] + + +class BrowserNewContextParamsRecordVideoShowActions(TypedDict): + duration: NotRequired[Optional[float]] + position: NotRequired[ + Optional[ + Literal[ + "top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right" + ] + ] + ] + fontSize: NotRequired[Optional[int]] + cursor: NotRequired[Optional[Literal["none", "pointer"]]] + style: NotRequired[Optional[BrowserNewContextParamsRecordVideoShowActionsStyle]] + + +class BrowserNewContextParamsRecordVideo(TypedDict): + dir: NotRequired[Optional[str]] + size: NotRequired[Optional[BrowserNewContextParamsRecordVideoSize]] + fps: NotRequired[Optional[int]] + showActions: NotRequired[Optional[BrowserNewContextParamsRecordVideoShowActions]] + + +class BrowserNewContextParamsProxy(TypedDict): + server: str + bypass: NotRequired[Optional[str]] + username: NotRequired[Optional[str]] + password: NotRequired[Optional[str]] + + +class BrowserNewContextParamsStorageState(TypedDict): + cookies: NotRequired[Optional[Sequence[SetNetworkCookie]]] + origins: NotRequired[Optional[Sequence[SetOriginStorage]]] + credentials: NotRequired[Optional[Sequence[SetVirtualCredential]]] + + +class BrowserNewContextResult(TypedDict): + context: BrowserContextChannel + + +class BrowserNewContextForReuseParamsViewport(TypedDict): + width: int + height: int + + +class BrowserNewContextForReuseParamsScreen(TypedDict): + width: int + height: int + + +class BrowserNewContextForReuseParamsClientCertificates(TypedDict): + origin: str + cert: NotRequired[str] + key: NotRequired[str] + passphrase: NotRequired[str] + pfx: NotRequired[str] + + +class BrowserNewContextForReuseParamsGeolocation(TypedDict): + longitude: float + latitude: float + accuracy: NotRequired[Optional[float]] + + +class BrowserNewContextForReuseParamsRecordVideoSize(TypedDict): + width: int + height: int + + +class BrowserNewContextForReuseParamsRecordVideoShowActionsStyle(TypedDict): + point: NotRequired[Optional[str]] + highlight: NotRequired[Optional[str]] + title: NotRequired[Optional[str]] + + +class BrowserNewContextForReuseParamsRecordVideoShowActions(TypedDict): + duration: NotRequired[Optional[float]] + position: NotRequired[ + Optional[ + Literal[ + "top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right" + ] + ] + ] + fontSize: NotRequired[Optional[int]] + cursor: NotRequired[Optional[Literal["none", "pointer"]]] + style: NotRequired[ + Optional[BrowserNewContextForReuseParamsRecordVideoShowActionsStyle] + ] + + +class BrowserNewContextForReuseParamsRecordVideo(TypedDict): + dir: NotRequired[Optional[str]] + size: NotRequired[Optional[BrowserNewContextForReuseParamsRecordVideoSize]] + fps: NotRequired[Optional[int]] + showActions: NotRequired[ + Optional[BrowserNewContextForReuseParamsRecordVideoShowActions] + ] + + +class BrowserNewContextForReuseParamsProxy(TypedDict): + server: str + bypass: NotRequired[Optional[str]] + username: NotRequired[Optional[str]] + password: NotRequired[Optional[str]] + + +class BrowserNewContextForReuseParamsStorageState(TypedDict): + cookies: NotRequired[Optional[Sequence[SetNetworkCookie]]] + origins: NotRequired[Optional[Sequence[SetOriginStorage]]] + credentials: NotRequired[Optional[Sequence[SetVirtualCredential]]] + + +class BrowserNewContextForReuseResult(TypedDict): + context: BrowserContextChannel + + +class BrowserNewBrowserCDPSessionResult(TypedDict): + session: CDPSessionChannel + + +class BrowserStopTracingResult(TypedDict): + artifact: ArtifactChannel + + +class BrowserContextEvent(TypedDict): + context: BrowserContextChannel + + +class BrowserContextInitializerOptionsViewport(TypedDict): + width: int + height: int + + +class BrowserContextInitializerOptionsScreen(TypedDict): + width: int + height: int + + +class BrowserContextInitializerOptionsClientCertificates(TypedDict): + origin: str + cert: NotRequired[str] + key: NotRequired[str] + passphrase: NotRequired[str] + pfx: NotRequired[str] + + +class BrowserContextInitializerOptionsGeolocation(TypedDict): + longitude: float + latitude: float + accuracy: NotRequired[float] + + +class BrowserContextInitializerOptionsRecordVideoSize(TypedDict): + width: int + height: int + + +class BrowserContextInitializerOptionsRecordVideoShowActionsStyle(TypedDict): + point: NotRequired[str] + highlight: NotRequired[str] + title: NotRequired[str] + + +class BrowserContextInitializerOptionsRecordVideoShowActions(TypedDict): + duration: NotRequired[float] + position: NotRequired[ + Literal["top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right"] + ] + fontSize: NotRequired[int] + cursor: NotRequired[Literal["none", "pointer"]] + style: NotRequired[BrowserContextInitializerOptionsRecordVideoShowActionsStyle] + + +class BrowserContextInitializerOptionsRecordVideo(TypedDict): + dir: NotRequired[str] + size: NotRequired[BrowserContextInitializerOptionsRecordVideoSize] + fps: NotRequired[int] + showActions: NotRequired[BrowserContextInitializerOptionsRecordVideoShowActions] + + +class BrowserContextInitializerOptions(TypedDict): + noDefaultViewport: NotRequired[bool] + viewport: NotRequired[BrowserContextInitializerOptionsViewport] + screen: NotRequired[BrowserContextInitializerOptionsScreen] + ignoreHTTPSErrors: NotRequired[bool] + clientCertificates: NotRequired[ + List[BrowserContextInitializerOptionsClientCertificates] + ] + javaScriptEnabled: NotRequired[bool] + bypassCSP: NotRequired[bool] + userAgent: NotRequired[str] + locale: NotRequired[str] + timezoneId: NotRequired[str] + geolocation: NotRequired[BrowserContextInitializerOptionsGeolocation] + permissions: NotRequired[List[str]] + extraHTTPHeaders: NotRequired[List[NameValue]] + offline: NotRequired[bool] + httpCredentials: NotRequired[List[HttpCredentials]] + deviceScaleFactor: NotRequired[float] + isMobile: NotRequired[bool] + hasTouch: NotRequired[bool] + colorScheme: NotRequired[Literal["dark", "light", "no-preference", "no-override"]] + reducedMotion: NotRequired[Literal["reduce", "no-preference", "no-override"]] + forcedColors: NotRequired[Literal["active", "none", "no-override"]] + acceptDownloads: NotRequired[Literal["accept", "deny", "internal-browser-default"]] + contrast: NotRequired[Literal["no-preference", "more", "no-override"]] + baseURL: NotRequired[str] + recordVideo: NotRequired[BrowserContextInitializerOptionsRecordVideo] + strictSelectors: NotRequired[bool] + serviceWorkers: NotRequired[Literal["allow", "block"]] + selectorEngines: NotRequired[List[SelectorEngine]] + testIdAttributeName: NotRequired[str] + + +class BrowserContextInitializer(TypedDict): + debugger: DebuggerChannel + requestContext: APIRequestContextChannel + tracing: TracingChannel + options: BrowserContextInitializerOptions + + +class BrowserContextAddInitScriptResult(TypedDict): + disposable: DisposableChannel + + +class BrowserContextCookiesResult(TypedDict): + cookies: List[NetworkCookie] + + +class BrowserContextExposeBindingResult(TypedDict): + disposable: DisposableChannel + + +class BrowserContextNewPageResult(TypedDict): + page: PageChannel + + +class BrowserContextSetGeolocationParamsGeolocation(TypedDict): + longitude: float + latitude: float + accuracy: NotRequired[Optional[float]] + + +class BrowserContextSetNetworkInterceptionPatternsParamsPatterns(TypedDict): + glob: NotRequired[str] + regexSource: NotRequired[str] + regexFlags: NotRequired[str] + urlPattern: NotRequired[URLPattern] + + +class BrowserContextSetWebSocketInterceptionPatternsParamsPatterns(TypedDict): + glob: NotRequired[str] + regexSource: NotRequired[str] + regexFlags: NotRequired[str] + urlPattern: NotRequired[URLPattern] + + +class BrowserContextStorageStateResult(TypedDict): + cookies: List[NetworkCookie] + origins: List[OriginStorage] + credentials: Optional[List[VirtualCredential]] + + +class BrowserContextSetStorageStateParamsStorageState(TypedDict): + cookies: NotRequired[Optional[Sequence[SetNetworkCookie]]] + origins: NotRequired[Optional[Sequence[SetOriginStorage]]] + credentials: NotRequired[Optional[Sequence[SetVirtualCredential]]] + + +class BrowserContextNewCDPSessionResult(TypedDict): + session: CDPSessionChannel + + +class BrowserContextCreateTempFilesParamsItems(TypedDict): + name: str + lastModifiedMs: NotRequired[float] + + +class BrowserContextCreateTempFilesResult(TypedDict): + rootDir: Optional[WritableStreamChannel] + writableStreams: List[WritableStreamChannel] + + +class BrowserContextCredentialsCreateResult(TypedDict): + credential: VirtualCredential + + +class BrowserContextCredentialsGetResult(TypedDict): + credentials: List[VirtualCredential] + + +class BrowserContextBindingCallEvent(TypedDict): + binding: BindingCallChannel + + +class BrowserContextConsoleEventLocation(TypedDict): + url: str + lineNumber: int + columnNumber: int + + +class BrowserContextConsoleEvent(TypedDict): + type: str + text: str + args: List[JSHandleChannel] + location: BrowserContextConsoleEventLocation + timestamp: float + page: Optional[PageChannel] + worker: Optional[WorkerChannel] + + +class BrowserContextDialogEvent(TypedDict): + dialog: DialogChannel + + +class BrowserContextDialogClosedEvent(TypedDict): + dialog: DialogChannel + + +class BrowserContextPageEvent(TypedDict): + page: PageChannel + + +class BrowserContextPageErrorEventLocation(TypedDict): + url: str + line: int + column: int + + +class BrowserContextPageErrorEvent(TypedDict): + error: SerializedError + page: PageChannel + location: BrowserContextPageErrorEventLocation + + +class BrowserContextRouteEvent(TypedDict): + route: RouteChannel + + +class BrowserContextWebSocketRouteEvent(TypedDict): + webSocketRoute: WebSocketRouteChannel + + +class BrowserContextServiceWorkerEvent(TypedDict): + worker: WorkerChannel + + +class BrowserContextRequestEvent(TypedDict): + request: RequestChannel + page: Optional[PageChannel] + + +class BrowserContextRequestFailedEvent(TypedDict): + request: RequestChannel + failureText: Optional[str] + responseEndTiming: float + page: Optional[PageChannel] + + +class BrowserContextRequestFinishedEvent(TypedDict): + request: RequestChannel + response: Optional[ResponseChannel] + responseEndTiming: float + page: Optional[PageChannel] + + +class BrowserContextResponseEvent(TypedDict): + response: ResponseChannel + page: Optional[PageChannel] + + +class BrowserContextRecorderEventEvent(TypedDict): + event: Literal["actionAdded", "actionUpdated", "signalAdded"] + data: Any + page: PageChannel + code: str + + +class BrowserTypeInitializer(TypedDict): + executablePath: str + name: str + + +class BrowserTypeLaunchParamsProxy(TypedDict): + server: str + bypass: NotRequired[Optional[str]] + username: NotRequired[Optional[str]] + password: NotRequired[Optional[str]] + + +class BrowserTypeLaunchResult(TypedDict): + browser: BrowserChannel + + +class BrowserTypeLaunchPersistentContextParamsProxy(TypedDict): + server: str + bypass: NotRequired[Optional[str]] + username: NotRequired[Optional[str]] + password: NotRequired[Optional[str]] + + +class BrowserTypeLaunchPersistentContextParamsViewport(TypedDict): + width: int + height: int + + +class BrowserTypeLaunchPersistentContextParamsScreen(TypedDict): + width: int + height: int + + +class BrowserTypeLaunchPersistentContextParamsClientCertificates(TypedDict): + origin: str + cert: NotRequired[str] + key: NotRequired[str] + passphrase: NotRequired[str] + pfx: NotRequired[str] + + +class BrowserTypeLaunchPersistentContextParamsGeolocation(TypedDict): + longitude: float + latitude: float + accuracy: NotRequired[Optional[float]] + + +class BrowserTypeLaunchPersistentContextParamsRecordVideoSize(TypedDict): + width: int + height: int + + +class BrowserTypeLaunchPersistentContextParamsRecordVideoShowActionsStyle(TypedDict): + point: NotRequired[Optional[str]] + highlight: NotRequired[Optional[str]] + title: NotRequired[Optional[str]] + + +class BrowserTypeLaunchPersistentContextParamsRecordVideoShowActions(TypedDict): + duration: NotRequired[Optional[float]] + position: NotRequired[ + Optional[ + Literal[ + "top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right" + ] + ] + ] + fontSize: NotRequired[Optional[int]] + cursor: NotRequired[Optional[Literal["none", "pointer"]]] + style: NotRequired[ + Optional[BrowserTypeLaunchPersistentContextParamsRecordVideoShowActionsStyle] + ] + + +class BrowserTypeLaunchPersistentContextParamsRecordVideo(TypedDict): + dir: NotRequired[Optional[str]] + size: NotRequired[Optional[BrowserTypeLaunchPersistentContextParamsRecordVideoSize]] + fps: NotRequired[Optional[int]] + showActions: NotRequired[ + Optional[BrowserTypeLaunchPersistentContextParamsRecordVideoShowActions] + ] + + +class BrowserTypeLaunchPersistentContextResult(TypedDict): + browser: BrowserChannel + context: BrowserContextChannel + + +class BrowserTypeConnectOverCDPResult(TypedDict): + browser: BrowserChannel + defaultContext: Optional[BrowserContextChannel] + + +class BrowserTypeConnectToWorkerResult(TypedDict): + worker: WorkerChannel + + +class MetadataLocation(TypedDict): + file: str + line: NotRequired[int] + column: NotRequired[int] + + +class Metadata(TypedDict): + location: NotRequired[MetadataLocation] + title: NotRequired[str] + internal: NotRequired[bool] + timeout: NotRequired[float] + + +class ClientSideCallMetadata(TypedDict): + id: str + stack: NotRequired[List[StackFrame]] + + +class WaitInfo(TypedDict): + waitId: str + phase: Literal["before", "after", "log"] + event: NotRequired[str] + message: NotRequired[str] + error: NotRequired[str] + + +class ElectronLaunchParamsGeolocation(TypedDict): + longitude: float + latitude: float + accuracy: NotRequired[Optional[float]] + + +class ElectronLaunchParamsRecordVideoSize(TypedDict): + width: int + height: int + + +class ElectronLaunchParamsRecordVideoShowActionsStyle(TypedDict): + point: NotRequired[Optional[str]] + highlight: NotRequired[Optional[str]] + title: NotRequired[Optional[str]] + + +class ElectronLaunchParamsRecordVideoShowActions(TypedDict): + duration: NotRequired[Optional[float]] + position: NotRequired[ + Optional[ + Literal[ + "top-left", "top", "top-right", "bottom-left", "bottom", "bottom-right" + ] + ] + ] + fontSize: NotRequired[Optional[int]] + cursor: NotRequired[Optional[Literal["none", "pointer"]]] + style: NotRequired[Optional[ElectronLaunchParamsRecordVideoShowActionsStyle]] + + +class ElectronLaunchParamsRecordVideo(TypedDict): + dir: NotRequired[Optional[str]] + size: NotRequired[Optional[ElectronLaunchParamsRecordVideoSize]] + fps: NotRequired[Optional[int]] + showActions: NotRequired[Optional[ElectronLaunchParamsRecordVideoShowActions]] + + +class ElectronLaunchResult(TypedDict): + electronApplication: ElectronApplicationChannel + + +class ElectronApplicationInitializer(TypedDict): + context: BrowserContextChannel + + +class ElectronApplicationBrowserWindowResult(TypedDict): + handle: JSHandleChannel + + +class ElectronApplicationEvaluateExpressionResult(TypedDict): + value: SerializedValue + + +class ElectronApplicationEvaluateExpressionHandleResult(TypedDict): + handle: JSHandleChannel + + +class ElectronApplicationConsoleEventLocation(TypedDict): + url: str + lineNumber: int + columnNumber: int + + +class ElectronApplicationConsoleEvent(TypedDict): + type: str + text: str + args: List[JSHandleChannel] + location: ElectronApplicationConsoleEventLocation + timestamp: float + + +class FrameInitializer(TypedDict): + url: str + name: str + parentFrame: NotRequired[FrameChannel] + loadStates: List[LifecycleEvent] + + +class FrameEvalOnSelectorResult(TypedDict): + value: SerializedValue + + +class FrameEvalOnSelectorAllResult(TypedDict): + value: SerializedValue + + +class FrameAddScriptTagResult(TypedDict): + element: ElementHandleChannel + + +class FrameAddStyleTagResult(TypedDict): + element: ElementHandleChannel + + +class FrameAriaSnapshotResult(TypedDict): + snapshot: str + + +class FrameAriaSnapshotJSONResult(TypedDict): + snapshot: Any + + +class FrameContentResult(TypedDict): + value: str + + +class FrameDropParamsPayloads(TypedDict): + name: str + mimeType: NotRequired[str] + buffer: str + + +class FrameDropParamsData(TypedDict): + mimeType: str + value: str + + +class FrameEvaluateExpressionResult(TypedDict): + value: SerializedValue + + +class FrameEvaluateExpressionHandleResult(TypedDict): + handle: JSHandleChannel + + +class FrameFrameElementResult(TypedDict): + element: ElementHandleChannel + + +class FrameResolveSelectorResult(TypedDict): + resolvedSelector: str + locatorCode: Optional[str] + + +class FrameGetAttributeResult(TypedDict): + value: Optional[str] + + +class FrameGotoResult(TypedDict): + response: Optional[ResponseChannel] + + +class FrameInnerHTMLResult(TypedDict): + value: str + + +class FrameInnerTextResult(TypedDict): + value: str + + +class FrameInputValueResult(TypedDict): + value: str + + +class FrameIsCheckedResult(TypedDict): + value: bool + + +class FrameIsDisabledResult(TypedDict): + value: bool + + +class FrameIsEnabledResult(TypedDict): + value: bool + + +class FrameIsHiddenResult(TypedDict): + value: bool + + +class FrameIsVisibleResult(TypedDict): + value: bool + + +class FrameIsEditableResult(TypedDict): + value: bool + + +class FrameQuerySelectorResult(TypedDict): + element: Optional[ElementHandleChannel] + + +class FrameQuerySelectorAllResult(TypedDict): + elements: List[ElementHandleChannel] + + +class FrameQueryCountResult(TypedDict): + value: int + + +class FrameSelectOptionParamsOptions(TypedDict): + valueOrLabel: NotRequired[str] + value: NotRequired[str] + label: NotRequired[str] + index: NotRequired[int] + + +class FrameSelectOptionResult(TypedDict): + values: List[str] + + +class FrameSetInputFilesParamsPayloads(TypedDict): + name: str + mimeType: NotRequired[str] + buffer: str + + +class FrameTextContentResult(TypedDict): + value: Optional[str] + + +class FrameTitleResult(TypedDict): + value: str + + +class FrameWaitForFunctionResult(TypedDict): + handle: Optional[JSHandleChannel] + + +class FrameWaitForSelectorResult(TypedDict): + element: Optional[ElementHandleChannel] + + +class FrameWebmcpToolsResultToolsAnnotations(TypedDict): + readOnly: NotRequired[bool] + untrustedContent: NotRequired[bool] + consequential: NotRequired[bool] + + +class FrameWebmcpToolsResultTools(TypedDict): + name: str + description: str + inputSchema: NotRequired[Any] + annotations: NotRequired[FrameWebmcpToolsResultToolsAnnotations] + + +class FrameWebmcpToolsResult(TypedDict): + tools: List[FrameWebmcpToolsResultTools] + + +class FrameWebmcpCallToolResult(TypedDict): + result: SerializedValue + + +class FrameLoadstateEvent(TypedDict): + add: Optional[LifecycleEvent] + remove: Optional[LifecycleEvent] + + +class FrameNavigatedEventNewDocument(TypedDict): + request: NotRequired[RequestChannel] + + +class FrameNavigatedEvent(TypedDict): + url: str + name: str + newDocument: Optional[FrameNavigatedEventNewDocument] + error: Optional[str] + + +class JSHandleInitializer(TypedDict): + preview: str + + +class JSHandleEvaluateExpressionResult(TypedDict): + value: SerializedValue + + +class JSHandleEvaluateExpressionHandleResult(TypedDict): + handle: JSHandleChannel + + +class JSHandleGetPropertyListResultProperties(TypedDict): + name: str + value: JSHandleChannel + + +class JSHandleGetPropertyListResult(TypedDict): + properties: List[JSHandleGetPropertyListResultProperties] + + +class JSHandleGetPropertyResult(TypedDict): + handle: JSHandleChannel + + +class JSHandleJsonValueResult(TypedDict): + value: SerializedValue + + +class JSHandlePreviewUpdatedEvent(TypedDict): + preview: str + + +class ElementHandleInitializer(JSHandleInitializer): + pass + + +class ElementHandleEvalOnSelectorResult(TypedDict): + value: SerializedValue + + +class ElementHandleEvalOnSelectorAllResult(TypedDict): + value: SerializedValue + + +class ElementHandleBoundingBoxResult(TypedDict): + value: Optional[Rect] + + +class ElementHandleContentFrameResult(TypedDict): + frame: Optional[FrameChannel] + + +class ElementHandleGetAttributeResult(TypedDict): + value: Optional[str] + + +class ElementHandleInnerHTMLResult(TypedDict): + value: str + + +class ElementHandleInnerTextResult(TypedDict): + value: str + + +class ElementHandleInputValueResult(TypedDict): + value: str + + +class ElementHandleIsCheckedResult(TypedDict): + value: bool + + +class ElementHandleIsDisabledResult(TypedDict): + value: bool + + +class ElementHandleIsEditableResult(TypedDict): + value: bool + + +class ElementHandleIsEnabledResult(TypedDict): + value: bool + + +class ElementHandleIsHiddenResult(TypedDict): + value: bool + + +class ElementHandleIsVisibleResult(TypedDict): + value: bool + + +class ElementHandleOwnerFrameResult(TypedDict): + frame: Optional[FrameChannel] + + +class ElementHandleQuerySelectorResult(TypedDict): + element: Optional[ElementHandleChannel] + + +class ElementHandleQuerySelectorAllResult(TypedDict): + elements: List[ElementHandleChannel] + + +class ElementHandleScreenshotParamsMask(TypedDict): + frame: FrameChannel + selector: str + + +class ElementHandleScreenshotResult(TypedDict): + binary: str + + +class ElementHandleSelectOptionParamsOptions(TypedDict): + valueOrLabel: NotRequired[str] + value: NotRequired[str] + label: NotRequired[str] + index: NotRequired[int] + + +class ElementHandleSelectOptionResult(TypedDict): + values: List[str] + + +class ElementHandleSetInputFilesParamsPayloads(TypedDict): + name: str + mimeType: NotRequired[str] + buffer: str + + +class ElementHandleTextContentResult(TypedDict): + value: Optional[str] + + +class ElementHandleWaitForSelectorResult(TypedDict): + element: Optional[ElementHandleChannel] + + +class LocalUtilsInitializerDeviceDescriptorsDescriptorViewport(TypedDict): + width: int + height: int + + +class LocalUtilsInitializerDeviceDescriptorsDescriptorScreen(TypedDict): + width: int + height: int + + +class LocalUtilsInitializerDeviceDescriptorsDescriptor(TypedDict): + userAgent: str + viewport: LocalUtilsInitializerDeviceDescriptorsDescriptorViewport + screen: NotRequired[LocalUtilsInitializerDeviceDescriptorsDescriptorScreen] + deviceScaleFactor: float + isMobile: bool + hasTouch: bool + defaultBrowserType: Literal["chromium", "firefox", "webkit"] + + +class LocalUtilsInitializerDeviceDescriptors(TypedDict): + name: str + descriptor: LocalUtilsInitializerDeviceDescriptorsDescriptor + + +class LocalUtilsInitializer(TypedDict): + deviceDescriptors: List[LocalUtilsInitializerDeviceDescriptors] + + +class LocalUtilsHarOpenResult(TypedDict): + harId: Optional[str] + error: Optional[str] + + +class LocalUtilsHarLookupResult(TypedDict): + action: Literal["error", "redirect", "fulfill", "noentry"] + message: Optional[str] + redirectURL: Optional[str] + status: Optional[int] + headers: Optional[List[NameValue]] + body: Optional[str] + + +class LocalUtilsConnectResult(TypedDict): + pipe: JsonPipeChannel + headers: List[NameValue] + + +class LocalUtilsTracingStartedResult(TypedDict): + stacksId: str + + +class LocalUtilsGlobToRegexResult(TypedDict): + regex: str + + +class LocalUtilsAsLocatorDescriptionResult(TypedDict): + description: str + + +class HttpCredentials(TypedDict): + username: str + password: str + origin: NotRequired[str] + send: NotRequired[Literal["always", "unauthorized"]] + + +class SetNetworkCookie(TypedDict): + name: str + value: str + url: NotRequired[str] + domain: NotRequired[str] + path: NotRequired[str] + expires: NotRequired[float] + httpOnly: NotRequired[bool] + secure: NotRequired[bool] + sameSite: NotRequired[Literal["Strict", "Lax", "None"]] + partitionKey: NotRequired[str] + _crHasCrossSiteAncestor: NotRequired[bool] + + +class NetworkCookie(TypedDict): + name: str + value: str + domain: str + path: str + expires: float + httpOnly: bool + secure: bool + sameSite: Literal["Strict", "Lax", "None"] + partitionKey: NotRequired[str] + _crHasCrossSiteAncestor: NotRequired[bool] + + +class RequestInitializer(TypedDict): + frame: NotRequired[FrameChannel] + serviceWorker: NotRequired[WorkerChannel] + url: str + resourceType: str + method: str + postData: NotRequired[str] + headers: List[NameValue] + isNavigationRequest: bool + redirectedFrom: NotRequired[RequestChannel] + + +class RequestResponseResult(TypedDict): + response: Optional[ResponseChannel] + + +class RequestRawRequestHeadersResult(TypedDict): + headers: List[NameValue] + + +class RouteInitializer(TypedDict): + request: RequestChannel + + +class WebSocketRouteInitializer(TypedDict): + url: str + protocols: List[str] + + +class WebSocketRouteMessageFromPageEvent(TypedDict): + message: str + isBase64: bool + + +class WebSocketRouteMessageFromServerEvent(TypedDict): + message: str + isBase64: bool + + +class WebSocketRouteClosePageEvent(TypedDict): + code: Optional[int] + reason: Optional[str] + wasClean: bool + + +class WebSocketRouteCloseServerEvent(TypedDict): + code: Optional[int] + reason: Optional[str] + wasClean: bool + + +class ResourceTiming(TypedDict): + startTime: float + domainLookupStart: float + domainLookupEnd: float + connectStart: float + secureConnectionStart: float + connectEnd: float + requestStart: float + responseStart: float + + +class ResponseInitializer(TypedDict): + request: RequestChannel + url: str + status: int + statusText: str + headers: List[NameValue] + timing: ResourceTiming + fromServiceWorker: bool + + +class ResponseBodyResult(TypedDict): + binary: str + + +class ResponseSecurityDetailsResult(TypedDict): + value: Optional[SecurityDetails] + + +class ResponseServerAddrResult(TypedDict): + value: Optional[RemoteAddr] + + +class ResponseRawResponseHeadersResult(TypedDict): + headers: List[NameValue] + + +class ResponseHttpVersionResult(TypedDict): + value: str + + +class ResponseSizesResult(TypedDict): + sizes: RequestSizes + + +class SecurityDetails(TypedDict): + issuer: NotRequired[str] + protocol: NotRequired[str] + subjectName: NotRequired[str] + validFrom: NotRequired[float] + validTo: NotRequired[float] + + +class RequestSizes(TypedDict): + requestBodySize: int + requestHeadersSize: int + responseBodySize: int + responseHeadersSize: int + + +class RemoteAddr(TypedDict): + ipAddress: str + port: int + + +class WebSocketInitializer(TypedDict): + url: str + + +class WebSocketFrameSentEvent(TypedDict): + opcode: int + data: str + + +class WebSocketFrameReceivedEvent(TypedDict): + opcode: int + data: str + + +class WebSocketSocketErrorEvent(TypedDict): + error: str + + +class PageInitializerViewportSize(TypedDict): + width: int + height: int + + +class PageInitializer(TypedDict): + mainFrame: FrameChannel + viewportSize: NotRequired[PageInitializerViewportSize] + isClosed: bool + opener: NotRequired[PageChannel] + video: NotRequired[ArtifactChannel] + + +class PageAddInitScriptResult(TypedDict): + disposable: DisposableChannel + + +class PageConsoleMessagesResultMessagesLocation(TypedDict): + url: str + lineNumber: int + columnNumber: int + + +class PageConsoleMessagesResultMessages(TypedDict): + type: str + text: str + args: List[JSHandleChannel] + location: PageConsoleMessagesResultMessagesLocation + timestamp: float + + +class PageConsoleMessagesResult(TypedDict): + messages: List[PageConsoleMessagesResultMessages] + + +class PageExposeBindingResult(TypedDict): + disposable: DisposableChannel + + +class PageGoBackResult(TypedDict): + response: Optional[ResponseChannel] + + +class PageGoForwardResult(TypedDict): + response: Optional[ResponseChannel] + + +class PageRegisterLocatorHandlerResult(TypedDict): + uid: int + + +class PageReloadResult(TypedDict): + response: Optional[ResponseChannel] + + +class PageExpectScreenshotParamsLocator(TypedDict): + frame: FrameChannel + selector: str + + +class PageExpectScreenshotParamsMask(TypedDict): + frame: FrameChannel + selector: str + + +class PageExpectScreenshotResult(TypedDict): + actual: Optional[str] + + +class PageScreenshotParamsMask(TypedDict): + frame: FrameChannel + selector: str + + +class PageScreenshotResult(TypedDict): + binary: str + + +class PageSetNetworkInterceptionPatternsParamsPatterns(TypedDict): + glob: NotRequired[str] + regexSource: NotRequired[str] + regexFlags: NotRequired[str] + urlPattern: NotRequired[URLPattern] + + +class PageSetWebSocketInterceptionPatternsParamsPatterns(TypedDict): + glob: NotRequired[str] + regexSource: NotRequired[str] + regexFlags: NotRequired[str] + urlPattern: NotRequired[URLPattern] + + +class PageSetViewportSizeParamsViewportSize(TypedDict): + width: int + height: int + + +class PagePageErrorsResult(TypedDict): + errors: List[SerializedError] + + +class PagePdfParamsMargin(TypedDict): + top: NotRequired[Optional[str]] + bottom: NotRequired[Optional[str]] + left: NotRequired[Optional[str]] + right: NotRequired[Optional[str]] + + +class PagePdfResult(TypedDict): + pdf: str + + +class PageRequestsResult(TypedDict): + requests: List[RequestChannel] + + +class PageStopJSCoverageResultEntriesFunctionsRanges(TypedDict): + startOffset: int + endOffset: int + count: int + + +class PageStopJSCoverageResultEntriesFunctions(TypedDict): + functionName: str + isBlockCoverage: bool + ranges: List[PageStopJSCoverageResultEntriesFunctionsRanges] + + +class PageStopJSCoverageResultEntries(TypedDict): + url: str + scriptId: str + source: NotRequired[str] + functions: List[PageStopJSCoverageResultEntriesFunctions] + + +class PageStopJSCoverageResult(TypedDict): + entries: List[PageStopJSCoverageResultEntries] + + +class PageStopCSSCoverageResultEntriesRanges(TypedDict): + start: int + end: int + + +class PageStopCSSCoverageResultEntries(TypedDict): + url: str + text: NotRequired[str] + ranges: List[PageStopCSSCoverageResultEntriesRanges] + + +class PageStopCSSCoverageResult(TypedDict): + entries: List[PageStopCSSCoverageResultEntries] + + +class PagePickLocatorResult(TypedDict): + selector: str + + +class PageScreencastShowOverlayResult(TypedDict): + id: str + + +class PageScreencastShowActionsParamsStyle(TypedDict): + point: NotRequired[Optional[str]] + highlight: NotRequired[Optional[str]] + title: NotRequired[Optional[str]] + + +class PageScreencastStartParamsSize(TypedDict): + width: int + height: int + + +class PageScreencastStartResult(TypedDict): + artifact: Optional[ArtifactChannel] + + +class PageWebStorageItemsResult(TypedDict): + items: List[NameValue] + + +class PageWebStorageGetItemResult(TypedDict): + value: Optional[str] + + +class PageBindingCallEvent(TypedDict): + binding: BindingCallChannel + + +class PageDownloadEvent(TypedDict): + url: str + suggestedFilename: str + artifact: ArtifactChannel + + +class PageViewportSizeChangedEventViewportSize(TypedDict): + width: int + height: int + + +class PageViewportSizeChangedEvent(TypedDict): + viewportSize: Optional[PageViewportSizeChangedEventViewportSize] + + +class PageFileChooserEvent(TypedDict): + element: ElementHandleChannel + isMultiple: bool + + +class PageFrameAttachedEvent(TypedDict): + frame: FrameChannel + + +class PageFrameDetachedEvent(TypedDict): + frame: FrameChannel + + +class PageLocatorHandlerTriggeredEvent(TypedDict): + uid: int + + +class PageRouteEvent(TypedDict): + route: RouteChannel + + +class PageScreencastFrameEvent(TypedDict): + frameId: int + data: str + timestamp: float + viewportWidth: int + viewportHeight: int + + +class PageWebSocketRouteEvent(TypedDict): + webSocketRoute: WebSocketRouteChannel + + +class PageWebSocketEvent(TypedDict): + webSocket: WebSocketChannel + + +class PageWorkerEvent(TypedDict): + worker: WorkerChannel + + +class RootInitializeResult(TypedDict): + playwright: PlaywrightChannel + + +class PlaywrightInitializer(TypedDict): + chromium: BrowserTypeChannel + firefox: BrowserTypeChannel + webkit: BrowserTypeChannel + android: AndroidChannel + electron: ElectronChannel + utils: NotRequired[LocalUtilsChannel] + preLaunchedBrowser: NotRequired[BrowserChannel] + preConnectedAndroidDevice: NotRequired[AndroidDeviceChannel] + socksSupport: NotRequired[SocksSupportChannel] + + +class PlaywrightNewRequestParamsClientCertificates(TypedDict): + origin: str + cert: NotRequired[str] + key: NotRequired[str] + passphrase: NotRequired[str] + pfx: NotRequired[str] + + +class PlaywrightNewRequestParamsProxy(TypedDict): + server: str + bypass: NotRequired[Optional[str]] + username: NotRequired[Optional[str]] + password: NotRequired[Optional[str]] + + +class PlaywrightNewRequestParamsStorageState(TypedDict): + cookies: NotRequired[Optional[Sequence[NetworkCookie]]] + origins: NotRequired[Optional[Sequence[SetOriginStorage]]] + + +class PlaywrightNewRequestResult(TypedDict): + request: APIRequestContextChannel + + +class DebugControllerInspectRequestedEvent(TypedDict): + selector: str + locator: str + ariaSnapshot: str + + +class DebugControllerSetModeRequestedEvent(TypedDict): + mode: str + + +class DebugControllerStateChangedEvent(TypedDict): + pageCount: int + + +class DebugControllerSourceChangedEvent(TypedDict): + text: str + header: Optional[str] + footer: Optional[str] + actions: Optional[List[str]] + + +class DebugControllerPausedEvent(TypedDict): + paused: bool + + +class SocksSupportSocksRequestedEvent(TypedDict): + uid: str + host: str + port: int + + +class SocksSupportSocksDataEvent(TypedDict): + uid: str + data: str + + +class SocksSupportSocksClosedEvent(TypedDict): + uid: str + + +class JsonPipeMessageEvent(TypedDict): + message: Any + + +class JsonPipeClosedEvent(TypedDict): + reason: Optional[str] + + +class ExpectedTextValue(TypedDict): + string: NotRequired[str] + regexSource: NotRequired[str] + regexFlags: NotRequired[str] + matchSubstring: NotRequired[bool] + ignoreCase: NotRequired[bool] + normalizeWhiteSpace: NotRequired[bool] + + +class SelectorEngine(TypedDict): + name: str + source: str + contentScript: NotRequired[bool] + + +class FormFieldFile(TypedDict): + name: str + mimeType: NotRequired[str] + buffer: str + + +class FormField(TypedDict): + name: str + value: NotRequired[str] + file: NotRequired[FormFieldFile] + + +class RecorderSourceHighlight(TypedDict): + line: int + type: str + + +class RecorderSource(TypedDict): + isRecorded: bool + id: str + label: str + text: str + language: str + highlight: List[RecorderSourceHighlight] + revealLine: NotRequired[int] + group: NotRequired[str] + + +class IndexedDBDatabaseStoresRecords(TypedDict): + key: NotRequired[Any] + keyEncoded: NotRequired[Any] + value: NotRequired[Any] + valueEncoded: NotRequired[Any] + + +class IndexedDBDatabaseStoresIndexes(TypedDict): + name: str + keyPath: NotRequired[str] + keyPathArray: NotRequired[List[str]] + multiEntry: bool + unique: bool + + +class IndexedDBDatabaseStores(TypedDict): + name: str + autoIncrement: bool + keyPath: NotRequired[str] + keyPathArray: NotRequired[List[str]] + records: List[IndexedDBDatabaseStoresRecords] + indexes: List[IndexedDBDatabaseStoresIndexes] + + +class IndexedDBDatabase(TypedDict): + name: str + version: int + stores: List[IndexedDBDatabaseStores] + + +class OPFSEntry(TypedDict): + path: str + type: Literal["file", "directory"] + base64: NotRequired[str] + + +class SetOriginStorage(TypedDict): + origin: str + localStorage: List[NameValue] + indexedDB: NotRequired[List[IndexedDBDatabase]] + opfs: NotRequired[List[OPFSEntry]] + + +class OriginStorage(TypedDict): + origin: str + localStorage: List[NameValue] + indexedDB: NotRequired[List[IndexedDBDatabase]] + opfs: NotRequired[List[OPFSEntry]] + + +class RecordHarOptions(TypedDict): + content: NotRequired[Literal["embed", "attach", "omit"]] + mode: NotRequired[Literal["full", "minimal"]] + urlGlob: NotRequired[str] + urlRegexSource: NotRequired[str] + urlRegexFlags: NotRequired[str] + harPath: NotRequired[str] + resourcesDir: NotRequired[str] + + +class CDPSessionSendResult(TypedDict): + result: Any + + +class CDPSessionEventEvent(TypedDict): + method: str + params: Optional[Any] + + +class BindingCallInitializer(TypedDict): + frame: FrameChannel + name: str + args: List[SerializedValue] + + +class DebuggerRunToParamsLocation(TypedDict): + file: str + line: NotRequired[Optional[int]] + column: NotRequired[Optional[int]] + + +class DebuggerPausedStateChangedEventPausedDetailsLocation(TypedDict): + file: str + line: NotRequired[int] + column: NotRequired[int] + + +class DebuggerPausedStateChangedEventPausedDetails(TypedDict): + location: DebuggerPausedStateChangedEventPausedDetailsLocation + title: str + stack: NotRequired[str] + + +class DebuggerPausedStateChangedEvent(TypedDict): + pausedDetails: Optional[DebuggerPausedStateChangedEventPausedDetails] + + +class DebuggerApiCallsUpdatedEventApiCallsLocation(TypedDict): + file: str + line: NotRequired[int] + column: NotRequired[int] + + +class DebuggerApiCallsUpdatedEventApiCalls(TypedDict): + id: str + title: str + location: NotRequired[DebuggerApiCallsUpdatedEventApiCallsLocation] + newLogEntries: List[str] + actionPoint: NotRequired[Point] + status: Literal["running", "success", "error"] + error: NotRequired[str] + + +class DebuggerApiCallsUpdatedEvent(TypedDict): + apiCalls: List[DebuggerApiCallsUpdatedEventApiCalls] + + +class DialogInitializer(TypedDict): + page: NotRequired[PageChannel] + type: str + message: str + defaultValue: str + + +class SerializedValueTa(TypedDict): + b: str + k: Literal[ + "i8", "ui8", "ui8c", "i16", "ui16", "i32", "ui32", "f32", "f64", "bi64", "bui64" + ] + + +class SerializedValueE(TypedDict): + m: str + n: str + s: str + + +class SerializedValueR(TypedDict): + p: str + f: str + + +class SerializedValueO(TypedDict): + k: str + v: SerializedValue + + +class SerializedValue(TypedDict): + n: NotRequired[float] + b: NotRequired[bool] + s: NotRequired[str] + v: NotRequired[Literal["null", "undefined", "NaN", "Infinity", "-Infinity", "-0"]] + d: NotRequired[str] + u: NotRequired[str] + bi: NotRequired[str] + ta: NotRequired[SerializedValueTa] + e: NotRequired[SerializedValueE] + r: NotRequired[SerializedValueR] + a: NotRequired[List[SerializedValue]] + o: NotRequired[List[SerializedValueO]] + h: NotRequired[int] + fn: NotRequired[str] + id: NotRequired[int] + ref: NotRequired[int] + + +class SerializedArgument(TypedDict): + value: SerializedValue + handles: List[Channel] + + +class SerializedErrorError(TypedDict): + message: str + name: str + stack: NotRequired[str] + code: NotRequired[str] + + +class SerializedError(TypedDict): + error: NotRequired[SerializedErrorError] + value: NotRequired[SerializedValue] + + +class StackFrame(TypedDict): + file: str + line: int + column: int + function: NotRequired[str] + + +class VirtualCredential(TypedDict): + id: str + rpId: str + userHandle: str + privateKey: str + publicKey: str + signCount: int + + +class SetVirtualCredential(TypedDict): + id: str + rpId: str + userHandle: str + privateKey: str + publicKey: str + signCount: NotRequired[int] + + +class Point(TypedDict): + x: float + y: float + + +class Rect(TypedDict): + x: float + y: float + width: float + height: float + + +class URLPattern(TypedDict): + hash: str + hostname: str + password: str + pathname: str + port: str + protocol: str + search: str + username: str + + +class NameValue(TypedDict): + name: str + value: str + + +class TracingTracingStartChunkResult(TypedDict): + traceName: str + + +class TracingTracingGroupParamsLocation(TypedDict): + file: str + line: NotRequired[Optional[int]] + column: NotRequired[Optional[int]] + + +class TracingTracingStopChunkResult(TypedDict): + artifact: Optional[ArtifactChannel] + entries: Optional[List[NameValue]] + + +class TracingHarStartResult(TypedDict): + harId: str + + +class TracingHarExportResult(TypedDict): + artifact: Optional[ArtifactChannel] + entries: Optional[List[NameValue]] + + +class WorkerInitializer(TypedDict): + url: str + + +class WorkerEvaluateExpressionResult(TypedDict): + value: SerializedValue + + +class WorkerEvaluateExpressionHandleResult(TypedDict): + handle: JSHandleChannel + + +class WorkerConsoleEventLocation(TypedDict): + url: str + lineNumber: int + columnNumber: int + + +class WorkerConsoleEvent(TypedDict): + type: str + text: str + args: List[JSHandleChannel] + location: WorkerConsoleEventLocation + timestamp: float diff --git a/playwright/_impl/_screencast.py b/playwright/_impl/_screencast.py index 2f39fc9da..1f56a927b 100644 --- a/playwright/_impl/_screencast.py +++ b/playwright/_impl/_screencast.py @@ -25,9 +25,9 @@ from playwright._impl._connection import from_nullable_channel from playwright._impl._disposable import DisposableStub from playwright._impl._errors import Error -from playwright._impl._helper import locals_to_params if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._page import Page @@ -55,12 +55,12 @@ def __init__(self, page: "Page") -> None: self._save_path: Optional[Union[str, Path]] = None self._on_frame: Optional[ScreencastFrameCallback] = None self._artifact: Optional[Artifact] = None - page._channel.on("screencastFrame", self._dispatch_frame) + page._channel.onScreencastFrame(self._dispatch_frame) def _ack_frame(self, frame_id: int) -> None: try: self._page._channel.send_no_reply( - "screencastFrameAck", None, {"frameId": frame_id} + "screencastFrameAck", {"frameId": frame_id} ) except (Error, OSError): pass @@ -71,65 +71,57 @@ async def _await_callback_and_ack(self, result: Any, frame_id: int) -> None: finally: self._ack_frame(frame_id) - def _dispatch_frame(self, params: dict) -> Any: + def _dispatch_frame(self, event: "channels.PageScreencastFrameEvent") -> Any: result = None task = None try: if not self._on_frame: return - data = params["data"] - if isinstance(data, str): - data = base64.b64decode(data) result = self._on_frame( { - "data": data, - "timestamp": params.get("timestamp", 0), - "viewportWidth": params["viewportWidth"], - "viewportHeight": params["viewportHeight"], + "data": base64.b64decode(event["data"]), + "timestamp": event["timestamp"], + "viewportWidth": event["viewportWidth"], + "viewportHeight": event["viewportHeight"], } ) finally: if hasattr(result, "__await__"): task = self._loop.create_task( - self._await_callback_and_ack(result, params["frameId"]) + self._await_callback_and_ack(result, event["frameId"]) ) else: - self._ack_frame(params["frameId"]) + self._ack_frame(event["frameId"]) return task async def start( self, - onFrame: ScreencastFrameCallback = None, - path: Union[str, Path] = None, - quality: int = None, - size: ScreencastSize = None, - fps: int = None, + on_frame: Optional[ScreencastFrameCallback], + path: Optional[Union[str, Path]], + quality: Optional[int], + size: Optional[ScreencastSize], + fps: Optional[int], ) -> DisposableStub: if self._started: raise Error("Screencast is already started") self._started = True - self._on_frame = onFrame - result = await self._page._channel.send_return_as_dict( - "screencastStart", - None, - { - "size": size, - "quality": quality, - "fps": fps, - "sendFrames": bool(onFrame), - "record": bool(path), - }, + self._on_frame = on_frame + result = await self._page._channel.screencastStart( + size=size, + quality=quality, + fps=fps, + sendFrames=bool(on_frame), + record=bool(path), ) - artifact_channel = (result or {}).get("artifact") - if artifact_channel: - self._artifact = from_nullable_channel(artifact_channel) + if result["artifact"]: + self._artifact = from_nullable_channel(result["artifact"]) self._save_path = path return DisposableStub(lambda: self.stop(), self._page) async def stop(self) -> None: self._started = False self._on_frame = None - await self._page._channel.send("screencastStop", None) + await self._page._channel.screencastStop() if self._save_path and self._artifact: await self._artifact.save_as(self._save_path) self._artifact = None @@ -137,48 +129,48 @@ async def stop(self) -> None: async def show_actions( self, - duration: float = None, - position: ScreencastPosition = None, - fontSize: int = None, - cursor: ScreencastCursor = None, - style: ScreencastActionStyle = None, + duration: Optional[float], + position: Optional[ScreencastPosition], + font_size: Optional[int], + cursor: Optional[ScreencastCursor], + style: Optional[ScreencastActionStyle], ) -> DisposableStub: - await self._page._channel.send( - "screencastShowActions", None, locals_to_params(locals()) + await self._page._channel.screencastShowActions( + duration=duration, + position=position, + fontSize=font_size, + cursor=cursor, + style=style, ) return DisposableStub(lambda: self.hide_actions(), self._page) async def hide_actions(self) -> None: - await self._page._channel.send("screencastHideActions", None) + await self._page._channel.screencastHideActions() - async def show_overlay(self, html: str, duration: float = None) -> DisposableStub: - result = await self._page._channel.send_return_as_dict( - "screencastShowOverlay", None, locals_to_params(locals()) + async def show_overlay( + self, html: str, duration: Optional[float] + ) -> DisposableStub: + result = await self._page._channel.screencastShowOverlay( + html=html, duration=duration ) - overlay_id = (result or {}).get("id") + overlay_id = result["id"] return DisposableStub( - lambda: self._page._channel.send( - "screencastRemoveOverlay", None, {"id": overlay_id} - ), + lambda: self._page._channel.screencastRemoveOverlay(id=overlay_id), self._page, ) async def show_chapter( self, title: str, - description: str = None, - duration: float = None, + description: Optional[str], + duration: Optional[float], ) -> None: - await self._page._channel.send( - "screencastChapter", None, locals_to_params(locals()) + await self._page._channel.screencastChapter( + title=title, description=description, duration=duration ) async def show_overlays(self) -> None: - await self._page._channel.send( - "screencastSetOverlayVisible", None, {"visible": True} - ) + await self._page._channel.screencastSetOverlayVisible(visible=True) async def hide_overlays(self) -> None: - await self._page._channel.send( - "screencastSetOverlayVisible", None, {"visible": False} - ) + await self._page._channel.screencastSetOverlayVisible(visible=False) diff --git a/playwright/_impl/_selectors.py b/playwright/_impl/_selectors.py index c3bac78e5..b07379b25 100644 --- a/playwright/_impl/_selectors.py +++ b/playwright/_impl/_selectors.py @@ -14,54 +14,53 @@ import asyncio from pathlib import Path -from typing import Any, Dict, List, Optional, Set, Union +from typing import TYPE_CHECKING, Any, List, Optional, Set, Union from playwright._impl._browser_context import BrowserContext from playwright._impl._errors import Error from playwright._impl._helper import async_readfile from playwright._impl._locator import set_test_id_attribute_name +if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels + class Selectors: def __init__(self, loop: asyncio.AbstractEventLoop, dispatcher_fiber: Any) -> None: self._loop = loop self._contexts_for_selectors: Set[BrowserContext] = set() - self._selector_engines: List[Dict] = [] + self._selector_engines: List[channels.SelectorEngine] = [] self._dispatcher_fiber = dispatcher_fiber self._test_id_attribute_name: Optional[str] = None async def register( self, name: str, - script: str = None, - path: Union[str, Path] = None, - contentScript: bool = None, + script: Optional[str], + path: Optional[Union[str, Path]], + content_script: Optional[bool], ) -> None: if any(engine for engine in self._selector_engines if engine["name"] == name): raise Error( f'Selectors.register: "{name}" selector engine has been already registered' ) - if not script and not path: - raise Error("Either source or path should be specified") if path: - script = (await async_readfile(path)).decode() - engine: Dict[str, Any] = dict(name=name, source=script) - if contentScript: - engine["contentScript"] = contentScript + source = (await async_readfile(path)).decode() + elif script: + source = script + else: + raise Error("Either source or path should be specified") + engine: channels.SelectorEngine = {"name": name, "source": source} + if content_script: + engine["contentScript"] = content_script for context in self._contexts_for_selectors: - await context._channel.send( - "registerSelectorEngine", - None, - {"selectorEngine": engine}, - ) + await context._channel.registerSelectorEngine(selectorEngine=engine) self._selector_engines.append(engine) - def set_test_id_attribute(self, attributeName: str) -> None: - set_test_id_attribute_name(attributeName) - self._test_id_attribute_name = attributeName + def set_test_id_attribute(self, attribute_name: str) -> None: + set_test_id_attribute_name(attribute_name) + self._test_id_attribute_name = attribute_name for context in self._contexts_for_selectors: context._channel.send_no_reply( - "setTestIdAttributeName", - None, - {"testIdAttributeName": attributeName}, + "setTestIdAttributeName", {"testIdAttributeName": attribute_name} ) diff --git a/playwright/_impl/_set_input_files_helpers.py b/playwright/_impl/_set_input_files_helpers.py index 0924261b0..00a583930 100644 --- a/playwright/_impl/_set_input_files_helpers.py +++ b/playwright/_impl/_set_input_files_helpers.py @@ -18,7 +18,6 @@ from pathlib import Path from typing import ( TYPE_CHECKING, - Dict, List, Optional, Sequence, @@ -28,12 +27,14 @@ cast, ) -from playwright._impl._connection import Channel, from_channel +from playwright._impl._connection import from_channel from playwright._impl._errors import Error from playwright._impl._writable_stream import WritableStream if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels from playwright._impl._browser_context import BrowserContext + from playwright._impl._protocol_channels import WritableStreamChannel from playwright._impl._api_structures import FilePayload @@ -41,11 +42,11 @@ class InputFilesList(TypedDict, total=False): - streams: Optional[List[Channel]] - directoryStream: Optional[Channel] + streams: Optional[List["WritableStreamChannel"]] + directoryStream: Optional["WritableStreamChannel"] localDirectory: Optional[str] localPaths: Optional[List[str]] - payloads: Optional[List[Dict[str, Union[str, bytes]]]] + payloads: Optional[List["channels.ElementHandleSetInputFilesParamsPayloads"]] def _list_files(directory: str) -> List[str]: @@ -82,30 +83,25 @@ async def convert_input_files( (_list_files(local_directory) if local_directory else local_paths), ) streams = [] - result = await context._connection.wrap_api_call( - lambda: context._channel.send_return_as_dict( - "createTempFiles", - None, - { - "rootDirName": ( - os.path.basename(local_directory) - if local_directory - else None - ), - "items": list( - map( - lambda file: dict( - name=( - os.path.relpath(file, local_directory) - if local_directory - else os.path.basename(file) - ), - lastModifiedMs=int(os.path.getmtime(file) * 1000), - ), - files_to_stream, - ) - ), - }, + root_dir_name = ( + os.path.basename(local_directory) if local_directory else None + ) + temp_files: List[channels.BrowserContextCreateTempFilesParamsItems] = [ + { + "name": ( + os.path.relpath(file, local_directory) + if local_directory + else os.path.basename(file) + ), + "lastModifiedMs": int(os.path.getmtime(file) * 1000), + } + for file in files_to_stream + ] + result: channels.BrowserContextCreateTempFilesResult = ( + await context._connection.wrap_api_call( + lambda: context._channel.createTempFiles( + rootDirName=root_dir_name, items=temp_files + ) ) ) for i, file in enumerate(result["writableStreams"]): @@ -114,7 +110,7 @@ async def convert_input_files( streams.append(stream._channel) return InputFilesList( streams=None if local_directory else streams, - directoryStream=result.get("rootDir"), + directoryStream=result["rootDir"], ) return InputFilesList(localPaths=local_paths, localDirectory=local_directory) diff --git a/playwright/_impl/_stream.py b/playwright/_impl/_stream.py index ba0adb762..2b96d462c 100644 --- a/playwright/_impl/_stream.py +++ b/playwright/_impl/_stream.py @@ -14,12 +14,17 @@ import base64 from pathlib import Path -from typing import Dict, Union +from typing import TYPE_CHECKING, Dict, Union from playwright._impl._connection import ChannelOwner +if TYPE_CHECKING: # pragma: no cover + from playwright._impl._protocol_channels import StreamChannel + class Stream(ChannelOwner): + _channel: "StreamChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -29,7 +34,7 @@ async def save_as(self, path: Union[str, Path]) -> None: file = await self._loop.run_in_executor(None, lambda: open(path, "wb")) try: while True: - binary = await self._channel.send("read", None, {"size": 1024 * 1024}) + binary = (await self._channel.read(size=1024 * 1024))["binary"] if not binary: break await self._loop.run_in_executor( @@ -41,7 +46,7 @@ async def save_as(self, path: Union[str, Path]) -> None: async def read_all(self) -> bytes: binary = b"" while True: - chunk = await self._channel.send("read", None, {"size": 1024 * 1024}) + chunk = (await self._channel.read(size=1024 * 1024))["binary"] if not chunk: break binary += base64.b64decode(chunk) diff --git a/playwright/_impl/_tracing.py b/playwright/_impl/_tracing.py index 0ebd11818..a398da18e 100644 --- a/playwright/_impl/_tracing.py +++ b/playwright/_impl/_tracing.py @@ -13,7 +13,7 @@ # limitations under the License. import pathlib -from typing import Any, Dict, Literal, Optional, Pattern, Union, cast +from typing import TYPE_CHECKING, Dict, Literal, Optional, Pattern, Union, cast from playwright._impl._api_structures import Location from playwright._impl._artifact import Artifact @@ -24,10 +24,16 @@ ) from playwright._impl._disposable import DisposableStub from playwright._impl._errors import Error -from playwright._impl._helper import locals_to_params + +if TYPE_CHECKING: # pragma: no cover + from playwright._impl import _protocol_types as channels + from playwright._impl._page import Page + from playwright._impl._protocol_channels import TracingChannel class Tracing(ChannelOwner): + _channel: "TracingChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -42,105 +48,94 @@ def __init__( async def start( self, - name: str = None, - title: str = None, - snapshots: bool = None, - ariaSnapshots: bool = None, - screenSnapshots: bool = None, - screenshots: bool = None, - sources: bool = None, - live: bool = None, + name: Optional[str], + title: Optional[str], + snapshots: Optional[bool], + aria_snapshots: Optional[bool], + screen_snapshots: Optional[bool], + screenshots: Optional[bool], + sources: Optional[bool], + live: Optional[bool], ) -> DisposableStub: - params = locals_to_params(locals()) self._include_sources = bool(sources) self._is_live = bool(live) - await self._channel.send( - "tracingStart", - None, - { - "name": name, - "snapshotDom": snapshots, - "snapshotAria": ariaSnapshots, - "snapshotScreen": screenSnapshots, - "screencast": screenshots, - "live": live, - }, - ) - trace_name = await self._channel.send( - "tracingStartChunk", None, {"title": title, "name": name} + await self._channel.tracingStart( + name=name, + snapshotDom=snapshots, + snapshotAria=aria_snapshots, + snapshotScreen=screen_snapshots, + screencast=screenshots, + coverage=None, + live=live, ) - await self._start_collecting_stacks(trace_name) - return DisposableStub(lambda: self.stop(), self) + result = await self._channel.tracingStartChunk(name=name, title=title) + await self._start_collecting_stacks(result["traceName"]) + return DisposableStub(lambda: self.stop(path=None), self) - async def start_chunk(self, title: str = None, name: str = None) -> DisposableStub: - params = locals_to_params(locals()) - trace_name = await self._channel.send("tracingStartChunk", None, params) - await self._start_collecting_stacks(trace_name) - return DisposableStub(lambda: self.stop_chunk(), self) + async def start_chunk( + self, name: Optional[str], title: Optional[str] + ) -> DisposableStub: + result = await self._channel.tracingStartChunk(name=name, title=title) + await self._start_collecting_stacks(result["traceName"]) + return DisposableStub(lambda: self.stop_chunk(path=None), self) async def _start_collecting_stacks(self, trace_name: str) -> None: if not self._is_tracing: self._is_tracing = True self._connection.set_is_tracing(True) self._stacks_id = await self._connection.local_utils.tracing_started( - self._traces_dir, trace_name, self._is_live + traces_dir=self._traces_dir, trace_name=trace_name, live=self._is_live ) - async def stop_chunk(self, path: Union[pathlib.Path, str] = None) -> None: + async def stop_chunk(self, path: Optional[Union[pathlib.Path, str]]) -> None: await self._do_stop_chunk(path) - async def stop(self, path: Union[pathlib.Path, str] = None) -> None: + async def stop(self, path: Optional[Union[pathlib.Path, str]]) -> None: await self._do_stop_chunk(path) - await self._channel.send( - "tracingStop", - None, - ) + await self._channel.tracingStop() - async def _do_stop_chunk(self, file_path: Union[pathlib.Path, str] = None) -> None: + async def _do_stop_chunk( + self, file_path: Optional[Union[pathlib.Path, str]] + ) -> None: self._reset_stack_counter() if not file_path: # Not interested in any artifacts - await self._channel.send("tracingStopChunk", None, {"mode": "discard"}) + await self._channel.tracingStopChunk(mode="discard") if self._stacks_id: - await self._connection.local_utils.trace_discarded(self._stacks_id) + await self._connection.local_utils.trace_discarded( + stacks_id=self._stacks_id + ) return is_local = not self._connection.is_remote if is_local: - result = await self._channel.send_return_as_dict( - "tracingStopChunk", None, {"mode": "entries"} - ) + result = await self._channel.tracingStopChunk(mode="entries") + entries = result["entries"] + if entries is None: + raise Error("tracingStopChunk returned no entries") await self._connection.local_utils.zip( - { - "zipFile": str(file_path), - "entries": result["entries"], - "stacksId": self._stacks_id, - "mode": "write", - "includeSources": self._include_sources, - } + zip_file=str(file_path), + entries=entries, + stacks_id=self._stacks_id, + mode="write", + include_sources=self._include_sources, + additional_sources=None, ) return - result = await self._channel.send_return_as_dict( - "tracingStopChunk", - None, - { - "mode": "archive", - }, - ) + result = await self._channel.tracingStopChunk(mode="archive") - artifact = cast( - Optional[Artifact], - from_nullable_channel(result.get("artifact")), - ) + artifact = cast(Optional[Artifact], from_nullable_channel(result["artifact"])) # The artifact may be missing if the browser closed while stopping tracing. if not artifact: if self._stacks_id: - await self._connection.local_utils.trace_discarded(self._stacks_id) + await self._connection.local_utils.trace_discarded( + stacks_id=self._stacks_id + ) return # Save trace to the final local file. @@ -148,13 +143,12 @@ async def _do_stop_chunk(self, file_path: Union[pathlib.Path, str] = None) -> No await artifact.delete() await self._connection.local_utils.zip( - { - "zipFile": str(file_path), - "entries": [], - "stacksId": self._stacks_id, - "mode": "append", - "includeSources": self._include_sources, - } + zip_file=str(file_path), + entries=[], + stacks_id=self._stacks_id, + mode="append", + include_sources=self._include_sources, + additional_sources=None, ) def _reset_stack_counter(self) -> None: @@ -162,22 +156,24 @@ def _reset_stack_counter(self) -> None: self._is_tracing = False self._connection.set_is_tracing(False) - async def group(self, name: str, location: Location = None) -> DisposableStub: - await self._channel.send("tracingGroup", None, locals_to_params(locals())) + async def group(self, name: str, location: Optional[Location]) -> DisposableStub: + await self._channel.tracingGroup( + name=name, + location=cast( + "Optional[channels.TracingTracingGroupParamsLocation]", location + ), + ) return DisposableStub(lambda: self.group_end(), self) async def group_end(self) -> None: - await self._channel.send( - "tracingGroupEnd", - None, - ) + await self._channel.tracingGroupEnd() async def start_har( self, path: Union[pathlib.Path, str], - content: Literal["attach", "embed", "omit"] = None, - mode: Literal["full", "minimal"] = None, - urlFilter: Union[str, Pattern[str]] = None, + content: Optional[Literal["attach", "embed", "omit"]], + mode: Optional[Literal["full", "minimal"]], + url_filter: Optional[Union[str, Pattern[str]]], ) -> DisposableStub: if self._har_id: raise Error("HAR recording has already been started") @@ -188,7 +184,7 @@ async def start_har( self._har_id = await self._record_into_har( har=path, page=None, - url=urlFilter, + url=url_filter, update_content=content or default_content, update_mode=mode or "full", ) @@ -197,42 +193,33 @@ async def start_har( async def _record_into_har( self, har: Union[pathlib.Path, str], - page: Optional[ChannelOwner], + page: Optional["Page"], url: Union[str, Pattern[str]] = None, update_content: Literal["attach", "embed", "omit"] = None, update_mode: Literal["full", "minimal"] = None, resourcesDir: Optional[str] = None, ) -> str: is_zip = str(har).endswith(".zip") - url_glob: Optional[str] = None - url_regex_source: Optional[str] = None - url_regex_flags: Optional[str] = None + options: channels.RecordHarOptions = { + "content": update_content or "attach", + "mode": update_mode or "minimal", + } + if not is_zip: + options["harPath"] = str(har) if isinstance(url, str): - url_glob = url + options["urlGlob"] = url elif url is not None: - url_regex_source = url.pattern - url_regex_flags = "".join( + options["urlRegexSource"] = url.pattern + options["urlRegexFlags"] = "".join( flag for flag, mask in (("i", 2), ("m", 8), ("s", 16)) if url.flags & mask ) - options: Dict[str, object] = { - "content": update_content or "attach", - "mode": update_mode or "minimal", - "harPath": None if is_zip else str(har), - } - if url_glob is not None: - options["urlGlob"] = url_glob - if url_regex_source is not None: - options["urlRegexSource"] = url_regex_source - if url_regex_flags is not None: - options["urlRegexFlags"] = url_regex_flags if resourcesDir is not None: options["resourcesDir"] = resourcesDir - params: Dict[str, Any] = {"options": options} - if page is not None: - params["page"] = page._channel - result = await self._channel.send_return_as_dict("harStart", None, params) + result = await self._channel.harStart( + page=page._channel if page is not None else None, options=options + ) har_id = result["harId"] self._har_recorders[har_id] = {"path": str(har)} return har_id @@ -250,37 +237,38 @@ async def stop_har(self) -> None: await self._export_har(har_id) async def _export_har(self, har_id: str) -> None: - params = self._har_recorders.pop(har_id, None) - if not params: + recorder = self._har_recorders.pop(har_id, None) + if not recorder: return is_local = not self._connection.is_remote - is_zip = params["path"].endswith(".zip") + is_zip = recorder["path"].endswith(".zip") if is_local: - result = await self._channel.send_return_as_dict( - "harExport", None, {"harId": har_id, "mode": "entries"} - ) + result = await self._channel.harExport(harId=har_id, mode="entries") if not is_zip: # Server wrote HAR and resources to the user's chosen paths. return + entries = result["entries"] + if entries is None: + raise Error("harExport returned no entries") await self._connection.local_utils.zip( - { - "zipFile": params["path"], - "entries": result["entries"], - "mode": "write", - "includeSources": False, - } + zip_file=recorder["path"], + entries=entries, + stacks_id=None, + mode="write", + include_sources=False, + additional_sources=None, ) return - result = await self._channel.send_return_as_dict( - "harExport", None, {"harId": har_id, "mode": "archive"} - ) + result = await self._channel.harExport(harId=har_id, mode="archive") + if result["artifact"] is None: + raise Error("harExport returned no artifact") artifact = cast(Artifact, from_channel(result["artifact"])) if is_zip: - await artifact.save_as(params["path"]) + await artifact.save_as(recorder["path"]) await artifact.delete() return # Uncompressed har is not supported in thin clients - await artifact.save_as(params["path"] + ".tmp") + await artifact.save_as(recorder["path"] + ".tmp") await artifact.delete() diff --git a/playwright/_impl/_waiter.py b/playwright/_impl/_waiter.py index dcaffa197..a85b4d583 100644 --- a/playwright/_impl/_waiter.py +++ b/playwright/_impl/_waiter.py @@ -63,7 +63,6 @@ def _send_wait_for_event_info( try: self._channel.send_no_reply( "__waitInfo__", - None, params, is_internal=is_internal, title=title, diff --git a/playwright/_impl/_web_mcp.py b/playwright/_impl/_web_mcp.py index 6ae33d4f6..62355da64 100644 --- a/playwright/_impl/_web_mcp.py +++ b/playwright/_impl/_web_mcp.py @@ -12,10 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, List +from typing import TYPE_CHECKING, Any, List, Optional, cast from playwright._impl._api_structures import WebMCPTool -from playwright._impl._helper import locals_to_params from playwright._impl._js_handle import Serializable, parse_result, serialize_argument if TYPE_CHECKING: # pragma: no cover @@ -28,17 +27,18 @@ def __init__(self, frame: "Frame") -> None: self._loop = frame._loop self._dispatcher_fiber = frame._dispatcher_fiber - async def tools(self, timeout: float = None) -> List[WebMCPTool]: - return await self._frame._channel.send( - "webmcpTools", self._frame._timeout, locals_to_params(locals()) + async def tools(self, timeout: Optional[float]) -> List[WebMCPTool]: + result = await self._frame._channel.webmcpTools( + timeout=self._frame._timeout(timeout) ) + return cast(List[WebMCPTool], result["tools"]) async def call_tool( - self, name: str, input: Serializable = None, timeout: float = None + self, name: str, input: Serializable, timeout: Optional[float] ) -> Any: - result = await self._frame._channel.send( - "webmcpCallTool", - self._frame._timeout, - {"name": name, "input": serialize_argument(input), "timeout": timeout}, + result = await self._frame._channel.webmcpCallTool( + name=name, + input=serialize_argument(input), + timeout=self._frame._timeout(timeout), ) - return parse_result(result) + return parse_result(result["result"]) diff --git a/playwright/_impl/_web_storage.py b/playwright/_impl/_web_storage.py index 9c8a3832b..2a9f67257 100644 --- a/playwright/_impl/_web_storage.py +++ b/playwright/_impl/_web_storage.py @@ -16,7 +16,7 @@ from playwright._impl._api_structures import WebStorageItem -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from playwright._impl._page import Page @@ -26,33 +26,25 @@ class WebStorage: def __init__(self, page: "Page", kind: WebStorageKind) -> None: self._page = page - self._kind = kind + self._kind: WebStorageKind = kind self._loop = page._loop self._dispatcher_fiber = page._dispatcher_fiber async def items(self) -> List[WebStorageItem]: - result = await self._page._channel.send_return_as_dict( - "webStorageItems", None, {"kind": self._kind} - ) - return (result or {}).get("items", []) + result = await self._page._channel.webStorageItems(kind=self._kind) + return result["items"] async def get_item(self, name: str) -> Optional[str]: - result = await self._page._channel.send_return_as_dict( - "webStorageGetItem", None, {"kind": self._kind, "name": name} - ) - return (result or {}).get("value") + result = await self._page._channel.webStorageGetItem(kind=self._kind, name=name) + return result["value"] async def set_item(self, name: str, value: str) -> None: - await self._page._channel.send( - "webStorageSetItem", - None, - {"kind": self._kind, "name": name, "value": value}, + await self._page._channel.webStorageSetItem( + kind=self._kind, name=name, value=value ) async def remove_item(self, name: str) -> None: - await self._page._channel.send( - "webStorageRemoveItem", None, {"kind": self._kind, "name": name} - ) + await self._page._channel.webStorageRemoveItem(kind=self._kind, name=name) async def clear(self) -> None: - await self._page._channel.send("webStorageClear", None, {"kind": self._kind}) + await self._page._channel.webStorageClear(kind=self._kind) diff --git a/playwright/_impl/_writable_stream.py b/playwright/_impl/_writable_stream.py index 7d5b7704b..bd733fe8e 100644 --- a/playwright/_impl/_writable_stream.py +++ b/playwright/_impl/_writable_stream.py @@ -15,16 +15,21 @@ import base64 import os from pathlib import Path -from typing import Dict, Union +from typing import TYPE_CHECKING, Dict, Union from playwright._impl._connection import ChannelOwner +if TYPE_CHECKING: # pragma: no cover + from playwright._impl._protocol_channels import WritableStreamChannel + # COPY_BUFSIZE is taken from shutil.py in the standard library _WINDOWS = os.name == "nt" COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024 class WritableStream(ChannelOwner): + _channel: "WritableStreamChannel" + def __init__( self, parent: ChannelOwner, type: str, guid: str, initializer: Dict ) -> None: @@ -36,7 +41,5 @@ async def copy(self, path: Union[str, Path]) -> None: data = f.read(COPY_BUFSIZE) if not data: break - await self._channel.send( - "write", None, {"binary": base64.b64encode(data).decode()} - ) - await self._channel.send("close", None) + await self._channel.write(binary=base64.b64encode(data).decode()) + await self._channel.close() diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 6dd97eb99..54021313f 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -198,17 +198,17 @@ async def new_context( return mapping.from_impl( await self._impl_obj.new_context( - baseURL=base_url, - clientCertificates=mapping.to_impl(client_certificates), - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - failOnStatusCode=fail_on_status_code, - httpCredentials=mapping.to_impl(http_credentials), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, + base_url=base_url, + client_certificates=mapping.to_impl(client_certificates), + extra_http_headers=mapping.to_impl(extra_http_headers), + fail_on_status_code=fail_on_status_code, + http_credentials=mapping.to_impl(http_credentials), + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, proxy=proxy, - storageState=storage_state, + storage_state=storage_state, timeout=to_milliseconds(timeout), - userAgent=user_agent, + user_agent=user_agent, ) ) @@ -373,12 +373,12 @@ async def delete( await self._impl_obj.delete( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -504,14 +504,14 @@ async def fetch( return mapping.from_impl( await self._impl_obj.fetch( - urlOrRequest=mapping.to_impl(url_or_request), + url_or_request=mapping.to_impl(url_or_request), data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, method=method, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), @@ -607,12 +607,12 @@ async def get( await self._impl_obj.get( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -695,12 +695,12 @@ async def head( await self._impl_obj.head( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -783,12 +783,12 @@ async def patch( await self._impl_obj.patch( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -910,12 +910,12 @@ async def post( await self._impl_obj.post( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -998,12 +998,12 @@ async def put( await self._impl_obj.put( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -1039,7 +1039,7 @@ async def storage_state( return mapping.from_impl( await self._impl_obj.storage_state( - indexedDB=indexed_db, opfs=opfs, path=path + indexed_db=indexed_db, opfs=opfs, path=path ) ) @@ -1294,7 +1294,7 @@ async def bind( return mapping.from_impl( await self._impl_obj.bind( - title=title, host=host, port=port, workspaceDir=workspace_dir + title=title, host=host, port=port, workspace_dir=workspace_dir ) ) @@ -1600,42 +1600,42 @@ async def new_context( return mapping.from_impl( await self._impl_obj.new_context( - acceptDownloads=accept_downloads, - baseURL=base_url, - bypassCSP=bypass_csp, - clientCertificates=mapping.to_impl(client_certificates), - colorScheme=color_scheme, + accept_downloads=accept_downloads, + base_url=base_url, + bypass_csp=bypass_csp, + client_certificates=mapping.to_impl(client_certificates), + color_scheme=color_scheme, contrast=contrast, - defaultBrowserType=default_browser_type, - deviceScaleFactor=device_scale_factor, - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - forcedColors=forced_colors, + default_browser_type=default_browser_type, + device_scale_factor=device_scale_factor, + extra_http_headers=mapping.to_impl(extra_http_headers), + forced_colors=forced_colors, geolocation=geolocation, - hasTouch=has_touch, - httpCredentials=mapping.to_impl(http_credentials), - ignoreHTTPSErrors=ignore_https_errors, - isMobile=is_mobile, - javaScriptEnabled=java_script_enabled, + has_touch=has_touch, + http_credentials=mapping.to_impl(http_credentials), + ignore_https_errors=ignore_https_errors, + is_mobile=is_mobile, + java_script_enabled=java_script_enabled, locale=locale, - noViewport=no_viewport, + no_viewport=no_viewport, offline=offline, permissions=mapping.to_impl(permissions), proxy=proxy, - recordHarContent=record_har_content, - recordHarMode=record_har_mode, - recordHarOmitContent=record_har_omit_content, - recordHarPath=record_har_path, - recordHarUrlFilter=record_har_url_filter, - recordVideoDir=record_video_dir, - recordVideoFps=record_video_fps, - recordVideoSize=record_video_size, - reducedMotion=reduced_motion, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, + record_video_dir=record_video_dir, + record_video_fps=record_video_fps, + record_video_size=record_video_size, + reduced_motion=reduced_motion, screen=screen, - serviceWorkers=service_workers, - storageState=storage_state, - strictSelectors=strict_selectors, - timezoneId=timezone_id, - userAgent=user_agent, + service_workers=service_workers, + storage_state=storage_state, + strict_selectors=strict_selectors, + timezone_id=timezone_id, + user_agent=user_agent, viewport=viewport, ) ) @@ -1844,42 +1844,42 @@ async def new_page( return mapping.from_impl( await self._impl_obj.new_page( - acceptDownloads=accept_downloads, - baseURL=base_url, - bypassCSP=bypass_csp, - clientCertificates=mapping.to_impl(client_certificates), - colorScheme=color_scheme, + accept_downloads=accept_downloads, + base_url=base_url, + bypass_csp=bypass_csp, + client_certificates=mapping.to_impl(client_certificates), + color_scheme=color_scheme, contrast=contrast, - defaultBrowserType=default_browser_type, - deviceScaleFactor=device_scale_factor, - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - forcedColors=forced_colors, + default_browser_type=default_browser_type, + device_scale_factor=device_scale_factor, + extra_http_headers=mapping.to_impl(extra_http_headers), + forced_colors=forced_colors, geolocation=geolocation, - hasTouch=has_touch, - httpCredentials=mapping.to_impl(http_credentials), - ignoreHTTPSErrors=ignore_https_errors, - isMobile=is_mobile, - javaScriptEnabled=java_script_enabled, + has_touch=has_touch, + http_credentials=mapping.to_impl(http_credentials), + ignore_https_errors=ignore_https_errors, + is_mobile=is_mobile, + java_script_enabled=java_script_enabled, locale=locale, - noViewport=no_viewport, + no_viewport=no_viewport, offline=offline, permissions=mapping.to_impl(permissions), proxy=proxy, - recordHarContent=record_har_content, - recordHarMode=record_har_mode, - recordHarOmitContent=record_har_omit_content, - recordHarPath=record_har_path, - recordHarUrlFilter=record_har_url_filter, - recordVideoDir=record_video_dir, - recordVideoFps=record_video_fps, - recordVideoSize=record_video_size, - reducedMotion=reduced_motion, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, + record_video_dir=record_video_dir, + record_video_fps=record_video_fps, + record_video_size=record_video_size, + reduced_motion=reduced_motion, screen=screen, - serviceWorkers=service_workers, - storageState=storage_state, - strictSelectors=strict_selectors, - timezoneId=timezone_id, - userAgent=user_agent, + service_workers=service_workers, + storage_state=storage_state, + strict_selectors=strict_selectors, + timezone_id=timezone_id, + user_agent=user_agent, viewport=viewport, ) ) @@ -3397,10 +3397,10 @@ async def route_from_har( await self._impl_obj.route_from_har( har=har, - notFound=not_found, + not_found=not_found, update=update, - updateContent=update_content, - updateMode=update_mode, + update_content=update_content, + update_mode=update_mode, url=url, ) @@ -3587,7 +3587,7 @@ async def set_storage_state( information obtained via `browser_context.storage_state()`. """ - await self._impl_obj.set_storage_state(storageState=storage_state) + await self._impl_obj.set_storage_state(storage_state=storage_state) async def storage_state( self, @@ -3632,7 +3632,7 @@ async def storage_state( return mapping.from_impl( await self._impl_obj.storage_state( - credentials=credentials, indexedDB=indexed_db, opfs=opfs, path=path + credentials=credentials, indexed_db=indexed_db, opfs=opfs, path=path ) ) @@ -3962,9 +3962,9 @@ async def connect( return mapping.from_impl( await self._impl_obj.connect( endpoint=endpoint, - exposeNetwork=expose_network, + expose_network=expose_network, headers=mapping.to_impl(headers), - slowMo=slow_mo, + slow_mo=slow_mo, timeout=to_milliseconds(timeout), ) ) @@ -4036,12 +4036,12 @@ async def connect_over_cdp( return mapping.from_impl( await self._impl_obj.connect_over_cdp( - endpointURL=endpoint_url, - artifactsDir=artifacts_dir, + endpoint_url=endpoint_url, + artifacts_dir=artifacts_dir, headers=mapping.to_impl(headers), - isLocal=is_local, - noDefaults=no_defaults, - slowMo=slow_mo, + is_local=is_local, + no_defaults=no_defaults, + slow_mo=slow_mo, timeout=to_milliseconds(timeout), ) ) @@ -4180,22 +4180,22 @@ async def launch( return mapping.from_impl( await self._impl_obj.launch( args=mapping.to_impl(args), - artifactsDir=artifacts_dir, + artifacts_dir=artifacts_dir, channel=channel, - chromiumSandbox=chromium_sandbox, - downloadsPath=downloads_path, + chromium_sandbox=chromium_sandbox, + downloads_path=downloads_path, env=mapping.to_impl(env), - executablePath=executable_path, - firefoxUserPrefs=mapping.to_impl(firefox_user_prefs), - handleSIGHUP=handle_sighup, - handleSIGINT=handle_sigint, - handleSIGTERM=handle_sigterm, + executable_path=executable_path, + firefox_user_prefs=mapping.to_impl(firefox_user_prefs), + handle_sighup=handle_sighup, + handle_sigint=handle_sigint, + handle_sigterm=handle_sigterm, headless=headless, - ignoreDefaultArgs=mapping.to_impl(ignore_default_args), + ignore_default_args=mapping.to_impl(ignore_default_args), proxy=proxy, - slowMo=slow_mo, + slow_mo=slow_mo, timeout=to_milliseconds(timeout), - tracesDir=traces_dir, + traces_dir=traces_dir, ) ) @@ -4486,57 +4486,57 @@ async def launch_persistent_context( return mapping.from_impl( await self._impl_obj.launch_persistent_context( - userDataDir=user_data_dir, - acceptDownloads=accept_downloads, + user_data_dir=user_data_dir, + accept_downloads=accept_downloads, args=mapping.to_impl(args), - artifactsDir=artifacts_dir, - baseURL=base_url, - bypassCSP=bypass_csp, + artifacts_dir=artifacts_dir, + base_url=base_url, + bypass_csp=bypass_csp, channel=channel, - chromiumSandbox=chromium_sandbox, - clientCertificates=mapping.to_impl(client_certificates), - colorScheme=color_scheme, + chromium_sandbox=chromium_sandbox, + client_certificates=mapping.to_impl(client_certificates), + color_scheme=color_scheme, contrast=contrast, - deviceScaleFactor=device_scale_factor, - downloadsPath=downloads_path, + device_scale_factor=device_scale_factor, + downloads_path=downloads_path, env=mapping.to_impl(env), - executablePath=executable_path, - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - firefoxUserPrefs=mapping.to_impl(firefox_user_prefs), - forcedColors=forced_colors, + executable_path=executable_path, + extra_http_headers=mapping.to_impl(extra_http_headers), + firefox_user_prefs=mapping.to_impl(firefox_user_prefs), + forced_colors=forced_colors, geolocation=geolocation, - handleSIGHUP=handle_sighup, - handleSIGINT=handle_sigint, - handleSIGTERM=handle_sigterm, - hasTouch=has_touch, + handle_sighup=handle_sighup, + handle_sigint=handle_sigint, + handle_sigterm=handle_sigterm, + has_touch=has_touch, headless=headless, - httpCredentials=mapping.to_impl(http_credentials), - ignoreDefaultArgs=mapping.to_impl(ignore_default_args), - ignoreHTTPSErrors=ignore_https_errors, - isMobile=is_mobile, - javaScriptEnabled=java_script_enabled, + http_credentials=mapping.to_impl(http_credentials), + ignore_default_args=mapping.to_impl(ignore_default_args), + ignore_https_errors=ignore_https_errors, + is_mobile=is_mobile, + java_script_enabled=java_script_enabled, locale=locale, - noViewport=no_viewport, + no_viewport=no_viewport, offline=offline, permissions=mapping.to_impl(permissions), proxy=proxy, - recordHarContent=record_har_content, - recordHarMode=record_har_mode, - recordHarOmitContent=record_har_omit_content, - recordHarPath=record_har_path, - recordHarUrlFilter=record_har_url_filter, - recordVideoDir=record_video_dir, - recordVideoFps=record_video_fps, - recordVideoSize=record_video_size, - reducedMotion=reduced_motion, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, + record_video_dir=record_video_dir, + record_video_fps=record_video_fps, + record_video_size=record_video_size, + reduced_motion=reduced_motion, screen=screen, - serviceWorkers=service_workers, - slowMo=slow_mo, - strictSelectors=strict_selectors, + service_workers=service_workers, + slow_mo=slow_mo, + strict_selectors=strict_selectors, timeout=to_milliseconds(timeout), - timezoneId=timezone_id, - tracesDir=traces_dir, - userAgent=user_agent, + timezone_id=timezone_id, + traces_dir=traces_dir, + user_agent=user_agent, viewport=viewport, ) ) @@ -4983,12 +4983,12 @@ async def create( return mapping.from_impl( await self._impl_obj.create( - rpId=rp_id, + rp_id=rp_id, id=id, - privateKey=private_key, - publicKey=public_key, - signCount=sign_count, - userHandle=user_handle, + private_key=private_key, + public_key=public_key, + sign_count=sign_count, + user_handle=user_handle, ) ) @@ -5031,7 +5031,7 @@ async def get( List[{id: str, rpId: str, userHandle: str, privateKey: str, publicKey: str, signCount: int}] """ - return mapping.from_impl_list(await self._impl_obj.get(id=id, rpId=rp_id)) + return mapping.from_impl_list(await self._impl_obj.get(id=id, rp_id=rp_id)) async def install(self) -> None: """Credentials.install @@ -5174,7 +5174,7 @@ async def accept(self, prompt_text: typing.Optional[str] = None) -> None: A text to enter in prompt. Does not cause any effects if the dialog's `type` is not prompt. Optional. """ - await self._impl_obj.accept(promptText=prompt_text) + await self._impl_obj.accept(prompt_text=prompt_text) @property def default_value(self) -> str: @@ -5497,7 +5497,7 @@ async def get_property(self, property_name: str) -> "JSHandle": """ return mapping.from_impl( - await self._impl_obj.get_property(propertyName=property_name) + await self._impl_obj.get_property(property_name=property_name) ) async def json_value(self) -> typing.Any: @@ -5602,7 +5602,7 @@ async def check( await self._impl_obj.check( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -5679,11 +5679,11 @@ async def click( await self._impl_obj.click( button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -5772,7 +5772,7 @@ async def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -5827,7 +5827,7 @@ async def dispatch_event( """ await self._impl_obj.dispatch_event( - type=type, eventInit=mapping.to_impl(event_init) + type=type, event_init=mapping.to_impl(event_init) ) async def eval_on_selector( @@ -5957,7 +5957,7 @@ async def fill( await self._impl_obj.fill( value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -6041,7 +6041,7 @@ async def hover( await self._impl_obj.hover( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6234,7 +6234,7 @@ async def press( await self._impl_obj.press( key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -6360,8 +6360,8 @@ async def screenshot( animations=animations, caret=caret, mask=mapping.to_impl(mask), - maskColor=mask_color, - omitBackground=omit_background, + mask_color=mask_color, + omit_background=omit_background, path=path, quality=quality, scale=scale, @@ -6469,7 +6469,7 @@ async def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -6553,7 +6553,7 @@ async def set_checked( await self._impl_obj.set_checked( checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6597,7 +6597,7 @@ async def set_input_files( await self._impl_obj.set_input_files( files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -6658,7 +6658,7 @@ async def tap( await self._impl_obj.tap( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6711,7 +6711,7 @@ async def type( await self._impl_obj.type( text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -6765,7 +6765,7 @@ async def uncheck( await self._impl_obj.uncheck( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6948,7 +6948,7 @@ async def set_files( await self._impl_obj.set_files( files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -7089,7 +7089,7 @@ async def check( await self._impl_obj.check( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -7184,11 +7184,11 @@ async def click( await self._impl_obj.click( selector=selector, button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -7215,7 +7215,7 @@ async def content(self, *, include_shadow: typing.Optional[bool] = None) -> str: """ return mapping.from_maybe_impl( - await self._impl_obj.content(includeShadow=include_shadow) + await self._impl_obj.content(include_shadow=include_shadow) ) async def dblclick( @@ -7295,7 +7295,7 @@ async def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -7367,7 +7367,7 @@ async def dispatch_event( await self._impl_obj.dispatch_event( selector=selector, type=type, - eventInit=mapping.to_impl(event_init), + event_init=mapping.to_impl(event_init), strict=strict, timeout=to_milliseconds(timeout), ) @@ -7431,12 +7431,12 @@ async def drag_and_drop( source=source, target=target, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, scroll=scroll, - sourcePosition=source_position, + source_position=source_position, steps=steps, strict=strict, - targetPosition=target_position, + target_position=target_position, timeout=to_milliseconds(timeout), trial=trial, ) @@ -7704,7 +7704,7 @@ def expect_navigation( self._impl_obj.expect_navigation( timeout=to_milliseconds(timeout), url=self._wrap_handler(url), - waitUntil=wait_until, + wait_until=wait_until, ).future ) @@ -7755,7 +7755,7 @@ async def fill( selector=selector, value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -8213,7 +8213,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -8257,7 +8257,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -8425,7 +8425,7 @@ async def goto( url=url, referer=referer, timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) ) @@ -8495,7 +8495,7 @@ async def hover( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -8864,9 +8864,9 @@ def locator( self._impl_obj.locator( selector=selector, has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -8969,7 +8969,7 @@ async def press( selector=selector, key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9106,7 +9106,7 @@ async def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9175,7 +9175,7 @@ async def set_checked( selector=selector, checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -9217,7 +9217,7 @@ async def set_content( """ await self._impl_obj.set_content( - html=html, timeout=to_milliseconds(timeout), waitUntil=wait_until + html=html, timeout=to_milliseconds(timeout), wait_until=wait_until ) async def set_input_files( @@ -9265,7 +9265,7 @@ async def set_input_files( await self._impl_obj.set_input_files( selector=selector, files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9338,7 +9338,7 @@ async def tap( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -9435,7 +9435,7 @@ async def type( selector=selector, text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9499,7 +9499,7 @@ async def uncheck( await self._impl_obj.uncheck( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -9777,7 +9777,7 @@ async def wait_for_url( await self._impl_obj.wait_for_url( url=self._wrap_handler(url), timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) @property @@ -10148,7 +10148,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -10192,7 +10192,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -10359,11 +10359,11 @@ def locator( return mapping.from_impl( self._impl_obj.locator( - selectorOrLocator=mapping.to_impl(selector_or_locator), + selector_or_locator=mapping.to_impl(selector_or_locator), has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -10875,7 +10875,7 @@ async def check( await self._impl_obj.check( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -10922,7 +10922,7 @@ async def clear( """ await self._impl_obj.clear( - force=force, noWaitAfter=no_wait_after, timeout=to_milliseconds(timeout) + force=force, no_wait_after=no_wait_after, timeout=to_milliseconds(timeout) ) async def click( @@ -11017,11 +11017,11 @@ async def click( await self._impl_obj.click( button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -11151,7 +11151,7 @@ async def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -11265,7 +11265,7 @@ async def dispatch_event( await self._impl_obj.dispatch_event( type=type, - eventInit=mapping.to_impl(event_init), + event_init=mapping.to_impl(event_init), timeout=to_milliseconds(timeout), ) @@ -11340,11 +11340,11 @@ async def drag_to( await self._impl_obj.drag_to( target=target._impl_obj, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, scroll=scroll, - sourcePosition=source_position, + source_position=source_position, steps=steps, - targetPosition=target_position, + target_position=target_position, timeout=to_milliseconds(timeout), trial=trial, ) @@ -11621,7 +11621,7 @@ async def fill( await self._impl_obj.fill( value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -11686,9 +11686,9 @@ def filter( return mapping.from_impl( self._impl_obj.filter( has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, visible=visible, ) ) @@ -12099,7 +12099,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -12143,7 +12143,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -12343,7 +12343,7 @@ async def hover( await self._impl_obj.hover( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -12707,11 +12707,11 @@ def locator( return mapping.from_impl( self._impl_obj.locator( - selectorOrLocator=mapping.to_impl(selector_or_locator), + selector_or_locator=mapping.to_impl(selector_or_locator), has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -12878,7 +12878,7 @@ async def press( await self._impl_obj.press( key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -12932,7 +12932,7 @@ async def press_sequentially( await self._impl_obj.press_sequentially( text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -13034,8 +13034,8 @@ async def screenshot( animations=animations, caret=caret, mask=mapping.to_impl(mask), - maskColor=mask_color, - omitBackground=omit_background, + mask_color=mask_color, + omit_background=omit_background, path=path, quality=quality, scale=scale, @@ -13152,7 +13152,7 @@ async def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -13246,7 +13246,7 @@ async def set_checked( await self._impl_obj.set_checked( checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -13317,7 +13317,7 @@ async def set_input_files( await self._impl_obj.set_input_files( files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -13385,7 +13385,7 @@ async def tap( await self._impl_obj.tap( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -13453,7 +13453,7 @@ async def type( await self._impl_obj.type( text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) @@ -13517,7 +13517,7 @@ async def uncheck( await self._impl_obj.uncheck( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -13932,9 +13932,9 @@ async def not_to_contain_text( await self._impl_obj.not_to_contain_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) async def not_to_have_accessible_description( @@ -13962,7 +13962,7 @@ async def not_to_have_accessible_description( await self._impl_obj.not_to_have_accessible_description( description=description, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -13990,8 +13990,8 @@ async def not_to_have_accessible_error_message( __tracebackhide__ = True await self._impl_obj.not_to_have_accessible_error_message( - errorMessage=error_message, - ignoreCase=ignore_case, + error_message=error_message, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -14019,7 +14019,7 @@ async def not_to_have_accessible_name( __tracebackhide__ = True await self._impl_obj.not_to_have_accessible_name( - name=name, ignoreCase=ignore_case, timeout=to_milliseconds(timeout) + name=name, ignore_case=ignore_case, timeout=to_milliseconds(timeout) ) async def not_to_have_attribute( @@ -14051,7 +14051,7 @@ async def not_to_have_attribute( await self._impl_obj.not_to_have_attribute( name=name, value=value, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -14323,9 +14323,9 @@ async def not_to_have_text( await self._impl_obj.not_to_have_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) async def not_to_have_value( @@ -14867,9 +14867,9 @@ async def to_contain_text( await self._impl_obj.to_contain_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) async def to_have_accessible_description( @@ -14905,7 +14905,7 @@ async def to_have_accessible_description( await self._impl_obj.to_have_accessible_description( description=description, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -14941,8 +14941,8 @@ async def to_have_accessible_error_message( __tracebackhide__ = True await self._impl_obj.to_have_accessible_error_message( - errorMessage=error_message, - ignoreCase=ignore_case, + error_message=error_message, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -14978,7 +14978,7 @@ async def to_have_accessible_name( __tracebackhide__ = True await self._impl_obj.to_have_accessible_name( - name=name, ignoreCase=ignore_case, timeout=to_milliseconds(timeout) + name=name, ignore_case=ignore_case, timeout=to_milliseconds(timeout) ) async def to_have_attribute( @@ -15021,7 +15021,7 @@ async def to_have_attribute( await self._impl_obj.to_have_attribute( name=name, value=value, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -15414,9 +15414,9 @@ async def to_have_text( await self._impl_obj.to_have_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) async def to_have_value( @@ -15569,7 +15569,7 @@ async def click( """ await self._impl_obj.click( - x=x, y=y, button=button, clickCount=click_count, delay=delay + x=x, y=y, button=button, click_count=click_count, delay=delay ) async def dblclick( @@ -15616,7 +15616,7 @@ async def down( defaults to 1. See [UIEvent.detail]. """ - await self._impl_obj.down(button=button, clickCount=click_count) + await self._impl_obj.down(button=button, click_count=click_count) async def move( self, x: float, y: float, *, steps: typing.Optional[int] = None @@ -15656,7 +15656,7 @@ async def up( defaults to 1. See [UIEvent.detail]. """ - await self._impl_obj.up(button=button, clickCount=click_count) + await self._impl_obj.up(button=button, click_count=click_count) async def wheel(self, delta_x: float, delta_y: float) -> None: """Mouse.wheel @@ -15675,7 +15675,7 @@ async def wheel(self, delta_x: float, delta_y: float) -> None: Pixels to scroll vertically. """ - await self._impl_obj.wheel(deltaX=delta_x, deltaY=delta_y) + await self._impl_obj.wheel(delta_x=delta_x, delta_y=delta_y) mapping.register(MouseImpl, Mouse) @@ -15845,7 +15845,7 @@ async def handler(locator): await self._impl_obj.add_locator_handler( locator=locator._impl_obj, handler=self._wrap_handler(handler), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, times=times, ) @@ -16032,7 +16032,7 @@ async def check( await self._impl_obj.check( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -16135,11 +16135,11 @@ async def click( await self._impl_obj.click( selector=selector, button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -16184,7 +16184,7 @@ async def close( [before unload](https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload) page handlers. """ - await self._impl_obj.close(reason=reason, runBeforeUnload=run_before_unload) + await self._impl_obj.close(reason=reason, run_before_unload=run_before_unload) async def console_messages( self, *, filter: typing.Optional[Literal["all", "since-navigation"]] = None @@ -16226,7 +16226,7 @@ async def content(self, *, include_shadow: typing.Optional[bool] = None) -> str: """ return mapping.from_maybe_impl( - await self._impl_obj.content(includeShadow=include_shadow) + await self._impl_obj.content(include_shadow=include_shadow) ) @property @@ -16317,7 +16317,7 @@ async def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -16389,7 +16389,7 @@ async def dispatch_event( await self._impl_obj.dispatch_event( selector=selector, type=type, - eventInit=mapping.to_impl(event_init), + event_init=mapping.to_impl(event_init), strict=strict, timeout=to_milliseconds(timeout), ) @@ -16469,12 +16469,12 @@ async def drag_and_drop( source=source, target=target, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, scroll=scroll, - sourcePosition=source_position, + source_position=source_position, steps=steps, strict=strict, - targetPosition=target_position, + target_position=target_position, timeout=to_milliseconds(timeout), trial=trial, ) @@ -16543,11 +16543,11 @@ async def emulate_media( """ await self._impl_obj.emulate_media( - colorScheme=color_scheme, + color_scheme=color_scheme, contrast=contrast, - forcedColors=forced_colors, + forced_colors=forced_colors, media=media, - reducedMotion=reduced_motion, + reduced_motion=reduced_motion, ) async def eval_on_selector( @@ -17141,7 +17141,7 @@ def expect_navigation( self._impl_obj.expect_navigation( timeout=to_milliseconds(timeout), url=self._wrap_handler(url), - waitUntil=wait_until, + wait_until=wait_until, ).future ) @@ -17222,7 +17222,7 @@ def expect_request( return AsyncEventContextManager( self._impl_obj.expect_request( - urlOrPredicate=self._wrap_handler(url_or_predicate), + url_or_predicate=self._wrap_handler(url_or_predicate), timeout=to_milliseconds(timeout), ).future ) @@ -17306,7 +17306,7 @@ def expect_response( return AsyncEventContextManager( self._impl_obj.expect_response( - urlOrPredicate=self._wrap_handler(url_or_predicate), + url_or_predicate=self._wrap_handler(url_or_predicate), timeout=to_milliseconds(timeout), ).future ) @@ -17555,7 +17555,7 @@ async def fill( selector=selector, value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -18065,7 +18065,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -18109,7 +18109,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -18258,7 +18258,7 @@ async def go_back( return mapping.from_impl_nullable( await self._impl_obj.go_back( - timeout=to_milliseconds(timeout), waitUntil=wait_until + timeout=to_milliseconds(timeout), wait_until=wait_until ) ) @@ -18306,7 +18306,7 @@ async def go_forward( return mapping.from_impl_nullable( await self._impl_obj.go_forward( - timeout=to_milliseconds(timeout), waitUntil=wait_until + timeout=to_milliseconds(timeout), wait_until=wait_until ) ) @@ -18375,7 +18375,7 @@ async def goto( url=url, referer=referer, timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) ) @@ -18453,7 +18453,7 @@ async def hover( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -18858,9 +18858,9 @@ def locator( self._impl_obj.locator( selector=selector, has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -19607,18 +19607,18 @@ async def pdf( return mapping.from_maybe_impl( await self._impl_obj.pdf( - displayHeaderFooter=display_header_footer, - footerTemplate=footer_template, + display_header_footer=display_header_footer, + footer_template=footer_template, format=format, - headerTemplate=header_template, + header_template=header_template, height=height, landscape=landscape, margin=margin, outline=outline, - pageRanges=page_ranges, + page_ranges=page_ranges, path=path, - preferCSSPageSize=prefer_css_page_size, - printBackground=print_background, + prefer_css_page_size=prefer_css_page_size, + print_background=print_background, scale=scale, tagged=tagged, width=width, @@ -19719,7 +19719,7 @@ async def press( selector=selector, key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -19806,7 +19806,7 @@ async def reload( return mapping.from_impl_nullable( await self._impl_obj.reload( - timeout=to_milliseconds(timeout), waitUntil=wait_until + timeout=to_milliseconds(timeout), wait_until=wait_until ) ) @@ -20014,10 +20014,10 @@ async def route_from_har( await self._impl_obj.route_from_har( har=har, - notFound=not_found, + not_found=not_found, update=update, - updateContent=update_content, - updateMode=update_mode, + update_content=update_content, + update_mode=update_mode, url=url, ) @@ -20158,10 +20158,10 @@ async def screenshot( animations=animations, caret=caret, clip=clip, - fullPage=full_page, + full_page=full_page, mask=mapping.to_impl(mask), - maskColor=mask_color, - omitBackground=omit_background, + mask_color=mask_color, + omit_background=omit_background, path=path, quality=quality, scale=scale, @@ -20251,7 +20251,7 @@ async def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -20332,7 +20332,7 @@ async def set_checked( selector=selector, checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -20374,7 +20374,7 @@ async def set_content( """ await self._impl_obj.set_content( - html=html, timeout=to_milliseconds(timeout), waitUntil=wait_until + html=html, timeout=to_milliseconds(timeout), wait_until=wait_until ) def set_default_navigation_timeout( @@ -20480,7 +20480,7 @@ async def set_input_files( await self._impl_obj.set_input_files( selector=selector, files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -20509,7 +20509,7 @@ async def set_viewport_size(self, viewport_size: ViewportSize) -> None: viewport_size : {width: int, height: int} """ - await self._impl_obj.set_viewport_size(viewportSize=viewport_size) + await self._impl_obj.set_viewport_size(viewport_size=viewport_size) async def tap( self, @@ -20579,7 +20579,7 @@ async def tap( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -20686,7 +20686,7 @@ async def type( selector=selector, text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -20750,7 +20750,7 @@ async def uncheck( await self._impl_obj.uncheck( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -21340,7 +21340,7 @@ async def wait_for_url( await self._impl_obj.wait_for_url( url=self._wrap_handler(url), timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) @property @@ -21400,7 +21400,7 @@ async def not_to_have_title( __tracebackhide__ = True await self._impl_obj.not_to_have_title( - titleOrRegExp=title_or_reg_exp, timeout=to_milliseconds(timeout) + title_or_reg_exp=title_or_reg_exp, timeout=to_milliseconds(timeout) ) async def not_to_have_url( @@ -21427,8 +21427,8 @@ async def not_to_have_url( __tracebackhide__ = True await self._impl_obj.not_to_have_url( - urlOrRegExp=url_or_reg_exp, - ignoreCase=ignore_case, + url_or_reg_exp=url_or_reg_exp, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -21484,7 +21484,7 @@ async def to_have_title( __tracebackhide__ = True await self._impl_obj.to_have_title( - titleOrRegExp=title_or_reg_exp, timeout=to_milliseconds(timeout) + title_or_reg_exp=title_or_reg_exp, timeout=to_milliseconds(timeout) ) async def to_have_url( @@ -21521,8 +21521,8 @@ async def to_have_url( __tracebackhide__ = True await self._impl_obj.to_have_url( - urlOrRegExp=url_or_reg_exp, - ignoreCase=ignore_case, + url_or_reg_exp=url_or_reg_exp, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) @@ -22304,7 +22304,7 @@ async def abort(self, error_code: typing.Optional[str] = None) -> None: - `'failed'` - A generic failure occurred. """ - await self._impl_obj.abort(errorCode=error_code) + await self._impl_obj.abort(error_code=error_code) async def continue_( self, @@ -22363,7 +22363,7 @@ async def handle(route, request): await self._impl_obj.continue_( headers=mapping.to_impl(headers), method=method, - postData=mapping.to_impl(post_data), + post_data=mapping.to_impl(post_data), url=url, ) @@ -22452,7 +22452,7 @@ async def handle(route, request): await self._impl_obj.fallback( headers=mapping.to_impl(headers), method=method, - postData=mapping.to_impl(post_data), + post_data=mapping.to_impl(post_data), url=url, ) @@ -22518,10 +22518,10 @@ async def handle(route): return mapping.from_impl( await self._impl_obj.fetch( headers=mapping.to_impl(headers), - maxRedirects=max_redirects, - maxRetries=max_retries, + max_redirects=max_redirects, + max_retries=max_retries, method=method, - postData=mapping.to_impl(post_data), + post_data=mapping.to_impl(post_data), timeout=to_milliseconds(timeout), url=url, ) @@ -22581,7 +22581,7 @@ async def fulfill( await self._impl_obj.fulfill( body=body, - contentType=content_type, + content_type=content_type, headers=mapping.to_impl(headers), json=mapping.to_impl(json), path=path, @@ -22667,7 +22667,7 @@ async def show_actions( await self._impl_obj.show_actions( cursor=cursor, duration=duration, - fontSize=font_size, + font_size=font_size, position=position, style=style, ) @@ -22778,7 +22778,7 @@ async def start( return mapping.from_impl( await self._impl_obj.start( fps=fps, - onFrame=self._wrap_handler(on_frame), + on_frame=self._wrap_handler(on_frame), path=path, quality=quality, size=size, @@ -22873,7 +22873,7 @@ async def main(): """ await self._impl_obj.register( - name=name, script=script, contentScript=content_script, path=path + name=name, script=script, content_script=content_script, path=path ) def set_test_id_attribute(self, attribute_name: str) -> None: @@ -22888,7 +22888,7 @@ def set_test_id_attribute(self, attribute_name: str) -> None: `"data-pw,data-ti"`. """ - self._impl_obj.set_test_id_attribute(attributeName=attribute_name) + self._impl_obj.set_test_id_attribute(attribute_name=attribute_name) mapping.register(SelectorsImpl, Selectors) @@ -23032,10 +23032,10 @@ async def start( return mapping.from_impl( await self._impl_obj.start( - ariaSnapshots=aria_snapshots, + aria_snapshots=aria_snapshots, live=live, name=name, - screenSnapshots=screen_snapshots, + screen_snapshots=screen_snapshots, screenshots=screenshots, snapshots=snapshots, sources=sources, @@ -23136,7 +23136,7 @@ async def start_har( return mapping.from_impl( await self._impl_obj.start_har( - path=path, content=content, mode=mode, urlFilter=url_filter + path=path, content=content, mode=mode, url_filter=url_filter ) ) diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 794b48cbd..f989547e8 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -199,17 +199,17 @@ def new_context( return mapping.from_impl( self._sync( self._impl_obj.new_context( - baseURL=base_url, - clientCertificates=mapping.to_impl(client_certificates), - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - failOnStatusCode=fail_on_status_code, - httpCredentials=mapping.to_impl(http_credentials), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, + base_url=base_url, + client_certificates=mapping.to_impl(client_certificates), + extra_http_headers=mapping.to_impl(extra_http_headers), + fail_on_status_code=fail_on_status_code, + http_credentials=mapping.to_impl(http_credentials), + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, proxy=proxy, - storageState=storage_state, + storage_state=storage_state, timeout=to_milliseconds(timeout), - userAgent=user_agent, + user_agent=user_agent, ) ) ) @@ -376,12 +376,12 @@ def delete( self._impl_obj.delete( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -509,14 +509,14 @@ def fetch( return mapping.from_impl( self._sync( self._impl_obj.fetch( - urlOrRequest=mapping.to_impl(url_or_request), + url_or_request=mapping.to_impl(url_or_request), data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, method=method, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), @@ -614,12 +614,12 @@ def get( self._impl_obj.get( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -704,12 +704,12 @@ def head( self._impl_obj.head( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -794,12 +794,12 @@ def patch( self._impl_obj.patch( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -923,12 +923,12 @@ def post( self._impl_obj.post( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -1013,12 +1013,12 @@ def put( self._impl_obj.put( url=url, data=mapping.to_impl(data), - failOnStatusCode=fail_on_status_code, + fail_on_status_code=fail_on_status_code, form=mapping.to_impl(form), headers=mapping.to_impl(headers), - ignoreHTTPSErrors=ignore_https_errors, - maxRedirects=max_redirects, - maxRetries=max_retries, + ignore_https_errors=ignore_https_errors, + max_redirects=max_redirects, + max_retries=max_retries, multipart=mapping.to_impl(multipart), params=mapping.to_impl(params), timeout=to_milliseconds(timeout), @@ -1055,7 +1055,9 @@ def storage_state( return mapping.from_impl( self._sync( - self._impl_obj.storage_state(indexedDB=indexed_db, opfs=opfs, path=path) + self._impl_obj.storage_state( + indexed_db=indexed_db, opfs=opfs, path=path + ) ) ) @@ -1312,7 +1314,7 @@ def bind( return mapping.from_impl( self._sync( self._impl_obj.bind( - title=title, host=host, port=port, workspaceDir=workspace_dir + title=title, host=host, port=port, workspace_dir=workspace_dir ) ) ) @@ -1620,42 +1622,42 @@ def new_context( return mapping.from_impl( self._sync( self._impl_obj.new_context( - acceptDownloads=accept_downloads, - baseURL=base_url, - bypassCSP=bypass_csp, - clientCertificates=mapping.to_impl(client_certificates), - colorScheme=color_scheme, + accept_downloads=accept_downloads, + base_url=base_url, + bypass_csp=bypass_csp, + client_certificates=mapping.to_impl(client_certificates), + color_scheme=color_scheme, contrast=contrast, - defaultBrowserType=default_browser_type, - deviceScaleFactor=device_scale_factor, - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - forcedColors=forced_colors, + default_browser_type=default_browser_type, + device_scale_factor=device_scale_factor, + extra_http_headers=mapping.to_impl(extra_http_headers), + forced_colors=forced_colors, geolocation=geolocation, - hasTouch=has_touch, - httpCredentials=mapping.to_impl(http_credentials), - ignoreHTTPSErrors=ignore_https_errors, - isMobile=is_mobile, - javaScriptEnabled=java_script_enabled, + has_touch=has_touch, + http_credentials=mapping.to_impl(http_credentials), + ignore_https_errors=ignore_https_errors, + is_mobile=is_mobile, + java_script_enabled=java_script_enabled, locale=locale, - noViewport=no_viewport, + no_viewport=no_viewport, offline=offline, permissions=mapping.to_impl(permissions), proxy=proxy, - recordHarContent=record_har_content, - recordHarMode=record_har_mode, - recordHarOmitContent=record_har_omit_content, - recordHarPath=record_har_path, - recordHarUrlFilter=record_har_url_filter, - recordVideoDir=record_video_dir, - recordVideoFps=record_video_fps, - recordVideoSize=record_video_size, - reducedMotion=reduced_motion, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, + record_video_dir=record_video_dir, + record_video_fps=record_video_fps, + record_video_size=record_video_size, + reduced_motion=reduced_motion, screen=screen, - serviceWorkers=service_workers, - storageState=storage_state, - strictSelectors=strict_selectors, - timezoneId=timezone_id, - userAgent=user_agent, + service_workers=service_workers, + storage_state=storage_state, + strict_selectors=strict_selectors, + timezone_id=timezone_id, + user_agent=user_agent, viewport=viewport, ) ) @@ -1866,42 +1868,42 @@ def new_page( return mapping.from_impl( self._sync( self._impl_obj.new_page( - acceptDownloads=accept_downloads, - baseURL=base_url, - bypassCSP=bypass_csp, - clientCertificates=mapping.to_impl(client_certificates), - colorScheme=color_scheme, + accept_downloads=accept_downloads, + base_url=base_url, + bypass_csp=bypass_csp, + client_certificates=mapping.to_impl(client_certificates), + color_scheme=color_scheme, contrast=contrast, - defaultBrowserType=default_browser_type, - deviceScaleFactor=device_scale_factor, - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - forcedColors=forced_colors, + default_browser_type=default_browser_type, + device_scale_factor=device_scale_factor, + extra_http_headers=mapping.to_impl(extra_http_headers), + forced_colors=forced_colors, geolocation=geolocation, - hasTouch=has_touch, - httpCredentials=mapping.to_impl(http_credentials), - ignoreHTTPSErrors=ignore_https_errors, - isMobile=is_mobile, - javaScriptEnabled=java_script_enabled, + has_touch=has_touch, + http_credentials=mapping.to_impl(http_credentials), + ignore_https_errors=ignore_https_errors, + is_mobile=is_mobile, + java_script_enabled=java_script_enabled, locale=locale, - noViewport=no_viewport, + no_viewport=no_viewport, offline=offline, permissions=mapping.to_impl(permissions), proxy=proxy, - recordHarContent=record_har_content, - recordHarMode=record_har_mode, - recordHarOmitContent=record_har_omit_content, - recordHarPath=record_har_path, - recordHarUrlFilter=record_har_url_filter, - recordVideoDir=record_video_dir, - recordVideoFps=record_video_fps, - recordVideoSize=record_video_size, - reducedMotion=reduced_motion, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, + record_video_dir=record_video_dir, + record_video_fps=record_video_fps, + record_video_size=record_video_size, + reduced_motion=reduced_motion, screen=screen, - serviceWorkers=service_workers, - storageState=storage_state, - strictSelectors=strict_selectors, - timezoneId=timezone_id, - userAgent=user_agent, + service_workers=service_workers, + storage_state=storage_state, + strict_selectors=strict_selectors, + timezone_id=timezone_id, + user_agent=user_agent, viewport=viewport, ) ) @@ -3310,10 +3312,10 @@ def route_from_har( self._sync( self._impl_obj.route_from_har( har=har, - notFound=not_found, + not_found=not_found, update=update, - updateContent=update_content, - updateMode=update_mode, + update_content=update_content, + update_mode=update_mode, url=url, ) ) @@ -3503,7 +3505,7 @@ def set_storage_state( information obtained via `browser_context.storage_state()`. """ - self._sync(self._impl_obj.set_storage_state(storageState=storage_state)) + self._sync(self._impl_obj.set_storage_state(storage_state=storage_state)) def storage_state( self, @@ -3549,7 +3551,7 @@ def storage_state( return mapping.from_impl( self._sync( self._impl_obj.storage_state( - credentials=credentials, indexedDB=indexed_db, opfs=opfs, path=path + credentials=credentials, indexed_db=indexed_db, opfs=opfs, path=path ) ) ) @@ -3885,9 +3887,9 @@ def connect( self._sync( self._impl_obj.connect( endpoint=endpoint, - exposeNetwork=expose_network, + expose_network=expose_network, headers=mapping.to_impl(headers), - slowMo=slow_mo, + slow_mo=slow_mo, timeout=to_milliseconds(timeout), ) ) @@ -3961,12 +3963,12 @@ def connect_over_cdp( return mapping.from_impl( self._sync( self._impl_obj.connect_over_cdp( - endpointURL=endpoint_url, - artifactsDir=artifacts_dir, + endpoint_url=endpoint_url, + artifacts_dir=artifacts_dir, headers=mapping.to_impl(headers), - isLocal=is_local, - noDefaults=no_defaults, - slowMo=slow_mo, + is_local=is_local, + no_defaults=no_defaults, + slow_mo=slow_mo, timeout=to_milliseconds(timeout), ) ) @@ -4107,22 +4109,22 @@ def launch( self._sync( self._impl_obj.launch( args=mapping.to_impl(args), - artifactsDir=artifacts_dir, + artifacts_dir=artifacts_dir, channel=channel, - chromiumSandbox=chromium_sandbox, - downloadsPath=downloads_path, + chromium_sandbox=chromium_sandbox, + downloads_path=downloads_path, env=mapping.to_impl(env), - executablePath=executable_path, - firefoxUserPrefs=mapping.to_impl(firefox_user_prefs), - handleSIGHUP=handle_sighup, - handleSIGINT=handle_sigint, - handleSIGTERM=handle_sigterm, + executable_path=executable_path, + firefox_user_prefs=mapping.to_impl(firefox_user_prefs), + handle_sighup=handle_sighup, + handle_sigint=handle_sigint, + handle_sigterm=handle_sigterm, headless=headless, - ignoreDefaultArgs=mapping.to_impl(ignore_default_args), + ignore_default_args=mapping.to_impl(ignore_default_args), proxy=proxy, - slowMo=slow_mo, + slow_mo=slow_mo, timeout=to_milliseconds(timeout), - tracesDir=traces_dir, + traces_dir=traces_dir, ) ) ) @@ -4415,57 +4417,57 @@ def launch_persistent_context( return mapping.from_impl( self._sync( self._impl_obj.launch_persistent_context( - userDataDir=user_data_dir, - acceptDownloads=accept_downloads, + user_data_dir=user_data_dir, + accept_downloads=accept_downloads, args=mapping.to_impl(args), - artifactsDir=artifacts_dir, - baseURL=base_url, - bypassCSP=bypass_csp, + artifacts_dir=artifacts_dir, + base_url=base_url, + bypass_csp=bypass_csp, channel=channel, - chromiumSandbox=chromium_sandbox, - clientCertificates=mapping.to_impl(client_certificates), - colorScheme=color_scheme, + chromium_sandbox=chromium_sandbox, + client_certificates=mapping.to_impl(client_certificates), + color_scheme=color_scheme, contrast=contrast, - deviceScaleFactor=device_scale_factor, - downloadsPath=downloads_path, + device_scale_factor=device_scale_factor, + downloads_path=downloads_path, env=mapping.to_impl(env), - executablePath=executable_path, - extraHTTPHeaders=mapping.to_impl(extra_http_headers), - firefoxUserPrefs=mapping.to_impl(firefox_user_prefs), - forcedColors=forced_colors, + executable_path=executable_path, + extra_http_headers=mapping.to_impl(extra_http_headers), + firefox_user_prefs=mapping.to_impl(firefox_user_prefs), + forced_colors=forced_colors, geolocation=geolocation, - handleSIGHUP=handle_sighup, - handleSIGINT=handle_sigint, - handleSIGTERM=handle_sigterm, - hasTouch=has_touch, + handle_sighup=handle_sighup, + handle_sigint=handle_sigint, + handle_sigterm=handle_sigterm, + has_touch=has_touch, headless=headless, - httpCredentials=mapping.to_impl(http_credentials), - ignoreDefaultArgs=mapping.to_impl(ignore_default_args), - ignoreHTTPSErrors=ignore_https_errors, - isMobile=is_mobile, - javaScriptEnabled=java_script_enabled, + http_credentials=mapping.to_impl(http_credentials), + ignore_default_args=mapping.to_impl(ignore_default_args), + ignore_https_errors=ignore_https_errors, + is_mobile=is_mobile, + java_script_enabled=java_script_enabled, locale=locale, - noViewport=no_viewport, + no_viewport=no_viewport, offline=offline, permissions=mapping.to_impl(permissions), proxy=proxy, - recordHarContent=record_har_content, - recordHarMode=record_har_mode, - recordHarOmitContent=record_har_omit_content, - recordHarPath=record_har_path, - recordHarUrlFilter=record_har_url_filter, - recordVideoDir=record_video_dir, - recordVideoFps=record_video_fps, - recordVideoSize=record_video_size, - reducedMotion=reduced_motion, + record_har_content=record_har_content, + record_har_mode=record_har_mode, + record_har_omit_content=record_har_omit_content, + record_har_path=record_har_path, + record_har_url_filter=record_har_url_filter, + record_video_dir=record_video_dir, + record_video_fps=record_video_fps, + record_video_size=record_video_size, + reduced_motion=reduced_motion, screen=screen, - serviceWorkers=service_workers, - slowMo=slow_mo, - strictSelectors=strict_selectors, + service_workers=service_workers, + slow_mo=slow_mo, + strict_selectors=strict_selectors, timeout=to_milliseconds(timeout), - timezoneId=timezone_id, - tracesDir=traces_dir, - userAgent=user_agent, + timezone_id=timezone_id, + traces_dir=traces_dir, + user_agent=user_agent, viewport=viewport, ) ) @@ -4890,12 +4892,12 @@ def create( return mapping.from_impl( self._sync( self._impl_obj.create( - rpId=rp_id, + rp_id=rp_id, id=id, - privateKey=private_key, - publicKey=public_key, - signCount=sign_count, - userHandle=user_handle, + private_key=private_key, + public_key=public_key, + sign_count=sign_count, + user_handle=user_handle, ) ) ) @@ -4939,7 +4941,9 @@ def get( List[{id: str, rpId: str, userHandle: str, privateKey: str, publicKey: str, signCount: int}] """ - return mapping.from_impl_list(self._sync(self._impl_obj.get(id=id, rpId=rp_id))) + return mapping.from_impl_list( + self._sync(self._impl_obj.get(id=id, rp_id=rp_id)) + ) def install(self) -> None: """Credentials.install @@ -5062,7 +5066,7 @@ def accept(self, prompt_text: typing.Optional[str] = None) -> None: A text to enter in prompt. Does not cause any effects if the dialog's `type` is not prompt. Optional. """ - self._sync(self._impl_obj.accept(promptText=prompt_text)) + self._sync(self._impl_obj.accept(prompt_text=prompt_text)) @property def default_value(self) -> str: @@ -5387,7 +5391,7 @@ def get_property(self, property_name: str) -> "JSHandle": """ return mapping.from_impl( - self._sync(self._impl_obj.get_property(propertyName=property_name)) + self._sync(self._impl_obj.get_property(property_name=property_name)) ) def json_value(self) -> typing.Any: @@ -5493,7 +5497,7 @@ def check( self._sync( self._impl_obj.check( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -5572,11 +5576,11 @@ def click( self._sync( self._impl_obj.click( button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -5667,7 +5671,7 @@ def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -5724,7 +5728,7 @@ def dispatch_event( self._sync( self._impl_obj.dispatch_event( - type=type, eventInit=mapping.to_impl(event_init) + type=type, event_init=mapping.to_impl(event_init) ) ) @@ -5860,7 +5864,7 @@ def fill( self._impl_obj.fill( value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -5948,7 +5952,7 @@ def hover( self._impl_obj.hover( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6143,7 +6147,7 @@ def press( self._impl_obj.press( key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -6271,8 +6275,8 @@ def screenshot( animations=animations, caret=caret, mask=mapping.to_impl(mask), - maskColor=mask_color, - omitBackground=omit_background, + mask_color=mask_color, + omit_background=omit_background, path=path, quality=quality, scale=scale, @@ -6382,7 +6386,7 @@ def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -6470,7 +6474,7 @@ def set_checked( self._impl_obj.set_checked( checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6516,7 +6520,7 @@ def set_input_files( self._sync( self._impl_obj.set_input_files( files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -6579,7 +6583,7 @@ def tap( self._impl_obj.tap( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6634,7 +6638,7 @@ def type( self._impl_obj.type( text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -6690,7 +6694,7 @@ def uncheck( self._sync( self._impl_obj.uncheck( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -6879,7 +6883,7 @@ def set_files( self._sync( self._impl_obj.set_files( files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -7026,7 +7030,7 @@ def check( self._impl_obj.check( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -7123,11 +7127,11 @@ def click( self._impl_obj.click( selector=selector, button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -7155,7 +7159,7 @@ def content(self, *, include_shadow: typing.Optional[bool] = None) -> str: """ return mapping.from_maybe_impl( - self._sync(self._impl_obj.content(includeShadow=include_shadow)) + self._sync(self._impl_obj.content(include_shadow=include_shadow)) ) def dblclick( @@ -7236,7 +7240,7 @@ def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -7310,7 +7314,7 @@ def dispatch_event( self._impl_obj.dispatch_event( selector=selector, type=type, - eventInit=mapping.to_impl(event_init), + event_init=mapping.to_impl(event_init), strict=strict, timeout=to_milliseconds(timeout), ) @@ -7376,12 +7380,12 @@ def drag_and_drop( source=source, target=target, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, scroll=scroll, - sourcePosition=source_position, + source_position=source_position, steps=steps, strict=strict, - targetPosition=target_position, + target_position=target_position, timeout=to_milliseconds(timeout), trial=trial, ) @@ -7656,7 +7660,7 @@ def expect_navigation( self._impl_obj.expect_navigation( timeout=to_milliseconds(timeout), url=self._wrap_handler(url), - waitUntil=wait_until, + wait_until=wait_until, ).future, ) @@ -7708,7 +7712,7 @@ def fill( selector=selector, value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -8171,7 +8175,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -8215,7 +8219,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -8384,7 +8388,7 @@ def goto( url=url, referer=referer, timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) ) ) @@ -8456,7 +8460,7 @@ def hover( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -8838,9 +8842,9 @@ def locator( self._impl_obj.locator( selector=selector, has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -8944,7 +8948,7 @@ def press( selector=selector, key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9083,7 +9087,7 @@ def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9154,7 +9158,7 @@ def set_checked( selector=selector, checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -9198,7 +9202,7 @@ def set_content( self._sync( self._impl_obj.set_content( - html=html, timeout=to_milliseconds(timeout), waitUntil=wait_until + html=html, timeout=to_milliseconds(timeout), wait_until=wait_until ) ) @@ -9248,7 +9252,7 @@ def set_input_files( self._impl_obj.set_input_files( selector=selector, files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9323,7 +9327,7 @@ def tap( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -9424,7 +9428,7 @@ def type( selector=selector, text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -9490,7 +9494,7 @@ def uncheck( self._impl_obj.uncheck( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -9770,7 +9774,7 @@ def wait_for_url( self._impl_obj.wait_for_url( url=self._wrap_handler(url), timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) ) @@ -10142,7 +10146,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -10186,7 +10190,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -10353,11 +10357,11 @@ def locator( return mapping.from_impl( self._impl_obj.locator( - selectorOrLocator=mapping.to_impl(selector_or_locator), + selector_or_locator=mapping.to_impl(selector_or_locator), has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -10875,7 +10879,7 @@ def check( self._sync( self._impl_obj.check( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -10924,7 +10928,9 @@ def clear( self._sync( self._impl_obj.clear( - force=force, noWaitAfter=no_wait_after, timeout=to_milliseconds(timeout) + force=force, + no_wait_after=no_wait_after, + timeout=to_milliseconds(timeout), ) ) @@ -11021,11 +11027,11 @@ def click( self._sync( self._impl_obj.click( button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -11157,7 +11163,7 @@ def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, steps=steps, @@ -11273,7 +11279,7 @@ def dispatch_event( self._sync( self._impl_obj.dispatch_event( type=type, - eventInit=mapping.to_impl(event_init), + event_init=mapping.to_impl(event_init), timeout=to_milliseconds(timeout), ) ) @@ -11350,11 +11356,11 @@ def drag_to( self._impl_obj.drag_to( target=target._impl_obj, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, scroll=scroll, - sourcePosition=source_position, + source_position=source_position, steps=steps, - targetPosition=target_position, + target_position=target_position, timeout=to_milliseconds(timeout), trial=trial, ) @@ -11641,7 +11647,7 @@ def fill( self._impl_obj.fill( value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -11706,9 +11712,9 @@ def filter( return mapping.from_impl( self._impl_obj.filter( has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, visible=visible, ) ) @@ -12121,7 +12127,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -12165,7 +12171,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -12364,7 +12370,7 @@ def hover( self._impl_obj.hover( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -12729,11 +12735,11 @@ def locator( return mapping.from_impl( self._impl_obj.locator( - selectorOrLocator=mapping.to_impl(selector_or_locator), + selector_or_locator=mapping.to_impl(selector_or_locator), has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -12901,7 +12907,7 @@ def press( self._impl_obj.press( key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -12957,7 +12963,7 @@ def press_sequentially( self._impl_obj.press_sequentially( text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -13061,8 +13067,8 @@ def screenshot( animations=animations, caret=caret, mask=mapping.to_impl(mask), - maskColor=mask_color, - omitBackground=omit_background, + mask_color=mask_color, + omit_background=omit_background, path=path, quality=quality, scale=scale, @@ -13181,7 +13187,7 @@ def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -13279,7 +13285,7 @@ def set_checked( self._impl_obj.set_checked( checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -13352,7 +13358,7 @@ def set_input_files( self._sync( self._impl_obj.set_input_files( files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -13422,7 +13428,7 @@ def tap( self._impl_obj.tap( force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -13492,7 +13498,7 @@ def type( self._impl_obj.type( text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, timeout=to_milliseconds(timeout), ) ) @@ -13558,7 +13564,7 @@ def uncheck( self._sync( self._impl_obj.uncheck( force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, timeout=to_milliseconds(timeout), @@ -13991,9 +13997,9 @@ def not_to_contain_text( self._sync( self._impl_obj.not_to_contain_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) ) @@ -14023,7 +14029,7 @@ def not_to_have_accessible_description( self._sync( self._impl_obj.not_to_have_accessible_description( description=description, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -14053,8 +14059,8 @@ def not_to_have_accessible_error_message( self._sync( self._impl_obj.not_to_have_accessible_error_message( - errorMessage=error_message, - ignoreCase=ignore_case, + error_message=error_message, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -14084,7 +14090,7 @@ def not_to_have_accessible_name( self._sync( self._impl_obj.not_to_have_accessible_name( - name=name, ignoreCase=ignore_case, timeout=to_milliseconds(timeout) + name=name, ignore_case=ignore_case, timeout=to_milliseconds(timeout) ) ) @@ -14118,7 +14124,7 @@ def not_to_have_attribute( self._impl_obj.not_to_have_attribute( name=name, value=value, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -14404,9 +14410,9 @@ def not_to_have_text( self._sync( self._impl_obj.not_to_have_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) ) @@ -14970,9 +14976,9 @@ def to_contain_text( self._sync( self._impl_obj.to_contain_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) ) @@ -15010,7 +15016,7 @@ def to_have_accessible_description( self._sync( self._impl_obj.to_have_accessible_description( description=description, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -15048,8 +15054,8 @@ def to_have_accessible_error_message( self._sync( self._impl_obj.to_have_accessible_error_message( - errorMessage=error_message, - ignoreCase=ignore_case, + error_message=error_message, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -15087,7 +15093,7 @@ def to_have_accessible_name( self._sync( self._impl_obj.to_have_accessible_name( - name=name, ignoreCase=ignore_case, timeout=to_milliseconds(timeout) + name=name, ignore_case=ignore_case, timeout=to_milliseconds(timeout) ) ) @@ -15132,7 +15138,7 @@ def to_have_attribute( self._impl_obj.to_have_attribute( name=name, value=value, - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -15537,9 +15543,9 @@ def to_have_text( self._sync( self._impl_obj.to_have_text( expected=mapping.to_impl(expected), - ignoreCase=ignore_case, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), - useInnerText=use_inner_text, + use_inner_text=use_inner_text, ) ) @@ -15698,7 +15704,7 @@ def click( self._sync( self._impl_obj.click( - x=x, y=y, button=button, clickCount=click_count, delay=delay + x=x, y=y, button=button, click_count=click_count, delay=delay ) ) @@ -15746,7 +15752,7 @@ def down( defaults to 1. See [UIEvent.detail]. """ - self._sync(self._impl_obj.down(button=button, clickCount=click_count)) + self._sync(self._impl_obj.down(button=button, click_count=click_count)) def move(self, x: float, y: float, *, steps: typing.Optional[int] = None) -> None: """Mouse.move @@ -15784,7 +15790,7 @@ def up( defaults to 1. See [UIEvent.detail]. """ - self._sync(self._impl_obj.up(button=button, clickCount=click_count)) + self._sync(self._impl_obj.up(button=button, click_count=click_count)) def wheel(self, delta_x: float, delta_y: float) -> None: """Mouse.wheel @@ -15803,7 +15809,7 @@ def wheel(self, delta_x: float, delta_y: float) -> None: Pixels to scroll vertically. """ - self._sync(self._impl_obj.wheel(deltaX=delta_x, deltaY=delta_y)) + self._sync(self._impl_obj.wheel(delta_x=delta_x, delta_y=delta_y)) mapping.register(MouseImpl, Mouse) @@ -15974,7 +15980,7 @@ def handler(locator): self._impl_obj.add_locator_handler( locator=locator._impl_obj, handler=self._wrap_handler(handler), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, times=times, ) ) @@ -16172,7 +16178,7 @@ def check( self._impl_obj.check( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -16277,11 +16283,11 @@ def click( self._impl_obj.click( selector=selector, button=button, - clickCount=click_count, + click_count=click_count, delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -16328,7 +16334,7 @@ def close( """ self._sync( - self._impl_obj.close(reason=reason, runBeforeUnload=run_before_unload) + self._impl_obj.close(reason=reason, run_before_unload=run_before_unload) ) def console_messages( @@ -16371,7 +16377,7 @@ def content(self, *, include_shadow: typing.Optional[bool] = None) -> str: """ return mapping.from_maybe_impl( - self._sync(self._impl_obj.content(includeShadow=include_shadow)) + self._sync(self._impl_obj.content(include_shadow=include_shadow)) ) @property @@ -16463,7 +16469,7 @@ def dblclick( delay=delay, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -16537,7 +16543,7 @@ def dispatch_event( self._impl_obj.dispatch_event( selector=selector, type=type, - eventInit=mapping.to_impl(event_init), + event_init=mapping.to_impl(event_init), strict=strict, timeout=to_milliseconds(timeout), ) @@ -16619,12 +16625,12 @@ def drag_and_drop( source=source, target=target, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, scroll=scroll, - sourcePosition=source_position, + source_position=source_position, steps=steps, strict=strict, - targetPosition=target_position, + target_position=target_position, timeout=to_milliseconds(timeout), trial=trial, ) @@ -16695,11 +16701,11 @@ def emulate_media( self._sync( self._impl_obj.emulate_media( - colorScheme=color_scheme, + color_scheme=color_scheme, contrast=contrast, - forcedColors=forced_colors, + forced_colors=forced_colors, media=media, - reducedMotion=reduced_motion, + reduced_motion=reduced_motion, ) ) @@ -17300,7 +17306,7 @@ def expect_navigation( self._impl_obj.expect_navigation( timeout=to_milliseconds(timeout), url=self._wrap_handler(url), - waitUntil=wait_until, + wait_until=wait_until, ).future, ) @@ -17379,7 +17385,7 @@ def expect_request( return EventContextManager( self, self._impl_obj.expect_request( - urlOrPredicate=self._wrap_handler(url_or_predicate), + url_or_predicate=self._wrap_handler(url_or_predicate), timeout=to_milliseconds(timeout), ).future, ) @@ -17461,7 +17467,7 @@ def expect_response( return EventContextManager( self, self._impl_obj.expect_response( - urlOrPredicate=self._wrap_handler(url_or_predicate), + url_or_predicate=self._wrap_handler(url_or_predicate), timeout=to_milliseconds(timeout), ).future, ) @@ -17709,7 +17715,7 @@ def fill( selector=selector, value=value, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -18224,7 +18230,7 @@ def get_by_role( disabled=disabled, exact=exact, expanded=expanded, - includeHidden=include_hidden, + include_hidden=include_hidden, level=level, name=name, pressed=pressed, @@ -18268,7 +18274,7 @@ def get_by_test_id( Locator """ - return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id)) + return mapping.from_impl(self._impl_obj.get_by_test_id(test_id=test_id)) def get_by_text( self, @@ -18418,7 +18424,7 @@ def go_back( return mapping.from_impl_nullable( self._sync( self._impl_obj.go_back( - timeout=to_milliseconds(timeout), waitUntil=wait_until + timeout=to_milliseconds(timeout), wait_until=wait_until ) ) ) @@ -18468,7 +18474,7 @@ def go_forward( return mapping.from_impl_nullable( self._sync( self._impl_obj.go_forward( - timeout=to_milliseconds(timeout), waitUntil=wait_until + timeout=to_milliseconds(timeout), wait_until=wait_until ) ) ) @@ -18539,7 +18545,7 @@ def goto( url=url, referer=referer, timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) ) ) @@ -18619,7 +18625,7 @@ def hover( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -19043,9 +19049,9 @@ def locator( self._impl_obj.locator( selector=selector, has=has._impl_obj if has else None, - hasNot=has_not._impl_obj if has_not else None, - hasNotText=has_not_text, - hasText=has_text, + has_not=has_not._impl_obj if has_not else None, + has_not_text=has_not_text, + has_text=has_text, ) ) @@ -19687,18 +19693,18 @@ def pdf( return mapping.from_maybe_impl( self._sync( self._impl_obj.pdf( - displayHeaderFooter=display_header_footer, - footerTemplate=footer_template, + display_header_footer=display_header_footer, + footer_template=footer_template, format=format, - headerTemplate=header_template, + header_template=header_template, height=height, landscape=landscape, margin=margin, outline=outline, - pageRanges=page_ranges, + page_ranges=page_ranges, path=path, - preferCSSPageSize=prefer_css_page_size, - printBackground=print_background, + prefer_css_page_size=prefer_css_page_size, + print_background=print_background, scale=scale, tagged=tagged, width=width, @@ -19801,7 +19807,7 @@ def press( selector=selector, key=key, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -19890,7 +19896,7 @@ def reload( return mapping.from_impl_nullable( self._sync( self._impl_obj.reload( - timeout=to_milliseconds(timeout), waitUntil=wait_until + timeout=to_milliseconds(timeout), wait_until=wait_until ) ) ) @@ -20102,10 +20108,10 @@ def route_from_har( self._sync( self._impl_obj.route_from_har( har=har, - notFound=not_found, + not_found=not_found, update=update, - updateContent=update_content, - updateMode=update_mode, + update_content=update_content, + update_mode=update_mode, url=url, ) ) @@ -20250,10 +20256,10 @@ def screenshot( animations=animations, caret=caret, clip=clip, - fullPage=full_page, + full_page=full_page, mask=mapping.to_impl(mask), - maskColor=mask_color, - omitBackground=omit_background, + mask_color=mask_color, + omit_background=omit_background, path=path, quality=quality, scale=scale, @@ -20345,7 +20351,7 @@ def select_option( force=force, index=mapping.to_impl(index), label=mapping.to_impl(label), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -20428,7 +20434,7 @@ def set_checked( selector=selector, checked=checked, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -20472,7 +20478,7 @@ def set_content( self._sync( self._impl_obj.set_content( - html=html, timeout=to_milliseconds(timeout), waitUntil=wait_until + html=html, timeout=to_milliseconds(timeout), wait_until=wait_until ) ) @@ -20582,7 +20588,7 @@ def set_input_files( self._impl_obj.set_input_files( selector=selector, files=mapping.to_impl(files), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -20612,7 +20618,7 @@ def set_viewport_size(self, viewport_size: ViewportSize) -> None: viewport_size : {width: int, height: int} """ - self._sync(self._impl_obj.set_viewport_size(viewportSize=viewport_size)) + self._sync(self._impl_obj.set_viewport_size(viewport_size=viewport_size)) def tap( self, @@ -20683,7 +20689,7 @@ def tap( selector=selector, force=force, modifiers=mapping.to_impl(modifiers), - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -20794,7 +20800,7 @@ def type( selector=selector, text=text, delay=delay, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, strict=strict, timeout=to_milliseconds(timeout), ) @@ -20860,7 +20866,7 @@ def uncheck( self._impl_obj.uncheck( selector=selector, force=force, - noWaitAfter=no_wait_after, + no_wait_after=no_wait_after, position=position, scroll=scroll, strict=strict, @@ -21456,7 +21462,7 @@ def wait_for_url( self._impl_obj.wait_for_url( url=self._wrap_handler(url), timeout=to_milliseconds(timeout), - waitUntil=wait_until, + wait_until=wait_until, ) ) @@ -21518,7 +21524,7 @@ def not_to_have_title( self._sync( self._impl_obj.not_to_have_title( - titleOrRegExp=title_or_reg_exp, timeout=to_milliseconds(timeout) + title_or_reg_exp=title_or_reg_exp, timeout=to_milliseconds(timeout) ) ) @@ -21547,8 +21553,8 @@ def not_to_have_url( self._sync( self._impl_obj.not_to_have_url( - urlOrRegExp=url_or_reg_exp, - ignoreCase=ignore_case, + url_or_reg_exp=url_or_reg_exp, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -21608,7 +21614,7 @@ def to_have_title( self._sync( self._impl_obj.to_have_title( - titleOrRegExp=title_or_reg_exp, timeout=to_milliseconds(timeout) + title_or_reg_exp=title_or_reg_exp, timeout=to_milliseconds(timeout) ) ) @@ -21647,8 +21653,8 @@ def to_have_url( self._sync( self._impl_obj.to_have_url( - urlOrRegExp=url_or_reg_exp, - ignoreCase=ignore_case, + url_or_reg_exp=url_or_reg_exp, + ignore_case=ignore_case, timeout=to_milliseconds(timeout), ) ) @@ -22436,7 +22442,7 @@ def abort(self, error_code: typing.Optional[str] = None) -> None: - `'failed'` - A generic failure occurred. """ - self._sync(self._impl_obj.abort(errorCode=error_code)) + self._sync(self._impl_obj.abort(error_code=error_code)) def continue_( self, @@ -22496,7 +22502,7 @@ def handle(route, request): self._impl_obj.continue_( headers=mapping.to_impl(headers), method=method, - postData=mapping.to_impl(post_data), + post_data=mapping.to_impl(post_data), url=url, ) ) @@ -22587,7 +22593,7 @@ def handle(route, request): self._impl_obj.fallback( headers=mapping.to_impl(headers), method=method, - postData=mapping.to_impl(post_data), + post_data=mapping.to_impl(post_data), url=url, ) ) @@ -22655,10 +22661,10 @@ def handle(route): self._sync( self._impl_obj.fetch( headers=mapping.to_impl(headers), - maxRedirects=max_redirects, - maxRetries=max_retries, + max_redirects=max_redirects, + max_retries=max_retries, method=method, - postData=mapping.to_impl(post_data), + post_data=mapping.to_impl(post_data), timeout=to_milliseconds(timeout), url=url, ) @@ -22720,7 +22726,7 @@ def fulfill( self._sync( self._impl_obj.fulfill( body=body, - contentType=content_type, + content_type=content_type, headers=mapping.to_impl(headers), json=mapping.to_impl(json), path=path, @@ -22808,7 +22814,7 @@ def show_actions( self._impl_obj.show_actions( cursor=cursor, duration=duration, - fontSize=font_size, + font_size=font_size, position=position, style=style, ) @@ -22923,7 +22929,7 @@ def start( self._sync( self._impl_obj.start( fps=fps, - onFrame=self._wrap_handler(on_frame), + on_frame=self._wrap_handler(on_frame), path=path, quality=quality, size=size, @@ -23016,7 +23022,7 @@ def run(playwright: Playwright): self._sync( self._impl_obj.register( - name=name, script=script, contentScript=content_script, path=path + name=name, script=script, content_script=content_script, path=path ) ) @@ -23032,7 +23038,7 @@ def set_test_id_attribute(self, attribute_name: str) -> None: `"data-pw,data-ti"`. """ - self._impl_obj.set_test_id_attribute(attributeName=attribute_name) + self._impl_obj.set_test_id_attribute(attribute_name=attribute_name) mapping.register(SelectorsImpl, Selectors) @@ -23177,10 +23183,10 @@ def start( return mapping.from_impl( self._sync( self._impl_obj.start( - ariaSnapshots=aria_snapshots, + aria_snapshots=aria_snapshots, live=live, name=name, - screenSnapshots=screen_snapshots, + screen_snapshots=screen_snapshots, screenshots=screenshots, snapshots=snapshots, sources=sources, @@ -23283,7 +23289,7 @@ def start_har( return mapping.from_impl( self._sync( self._impl_obj.start_har( - path=path, content=content, mode=mode, urlFilter=url_filter + path=path, content=content, mode=mode, url_filter=url_filter ) ) ) diff --git a/scripts/codegen/generate_api.js b/scripts/codegen/generate_api.js index bfdcfdd31..67effc7f7 100644 --- a/scripts/codegen/generate_api.js +++ b/scripts/codegen/generate_api.js @@ -56,7 +56,6 @@ const rules = require('./python_rules'); /** * @typedef {{ * name: string, - * implName: string, * type: PyType, * optional: boolean, * comment: string, @@ -620,7 +619,7 @@ function forwardExpression(param) { const name = param.name; if (someType(t, c => c.kind === 'callable')) return `self._wrap_handler(${name})`; - if (param.implName === 'timeout' && someType(t, c => c.kind === 'name' && c.name === 'float')) + if (param.name === 'timeout' && someType(t, c => c.kind === 'name' && c.name === 'float')) return `to_milliseconds(${name})`; if (someType(t, c => (c.kind === 'name' && c.name === 'Any') || @@ -688,7 +687,6 @@ function buildMember(clazz, member) { const nullable = type.kind === 'union' && !!type.members?.some(isNone); return /** @type {PyParam} */ ({ name: toSnakeCase(arg.alias), - implName: arg.name, type, optional: isOption || !arg.required || nullable, comment: renderSpec(arg.spec), @@ -706,7 +704,6 @@ function buildMember(clazz, member) { for (const extra of lookupRule('extraOptions', memberPlace) || []) { keywordOnly.push({ name: toSnakeCase(extra.name), - implName: extra.name, type: toPy(Type.parse(extra.type), { dir: 'in', place: `${memberPlace}.${extra.name}` }), optional: true, comment: '', @@ -749,7 +746,6 @@ function buildExtraMember(className, extra) { isAsync: extra.async, positional: extra.args.map(arg => ({ name: toSnakeCase(arg.name), - implName: arg.name, type: toPy(Type.parse(arg.type), { dir: 'in', place: `${place}.${arg.name}` }), optional: false, comment: '', @@ -891,7 +887,7 @@ function emitEventOverloads(clazz, methodName, out) { /** @param {PyParam} param */ function paramAnnotation(param) { let type = param.type; - if (param.implName === 'timeout' && type.kind === 'name' && type.name === 'float') + if (param.name === 'timeout' && type.kind === 'name' && type.name === 'float') type = makeUnion([NAME('float'), NAME('datetime.timedelta')]); if (param.optional && !(type.kind === 'union' && type.members?.some(isNone))) type = { kind: 'union', members: [type, NONE] }; @@ -958,7 +954,7 @@ function emitMember(clazz, member, out) { // Dunder methods take positional arguments only. const isDunder = member.name.startsWith('__'); - const callArgs = [...member.positional, ...member.keywordOnly].map(p => isDunder ? forwardExpression(p) : `${p.implName}=${forwardExpression(p)}`).join(', '); + const callArgs = [...member.positional, ...member.keywordOnly].map(p => isDunder ? forwardExpression(p) : `${p.name}=${forwardExpression(p)}`).join(', '); const call = `self._impl_obj.${member.name}(${callArgs})`; if (member.returnType?.kind === 'ecm') { if (isAsync) { diff --git a/scripts/codegen/generate_protocol.py b/scripts/codegen/generate_protocol.py index 17e51b779..35c8eb41f 100644 --- a/scripts/codegen/generate_protocol.py +++ b/scripts/codegen/generate_protocol.py @@ -12,21 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Generates playwright/_impl/_protocol.py from the protocol spec -# (packages/protocol/spec/*.yml in microsoft/playwright). +# Generates from the protocol spec (packages/protocol/spec/*.yml in +# microsoft/playwright): +# - playwright/_impl/_protocol.py: the tables that _protocol_check.py uses; +# - playwright/_impl/_protocol_types.py: the TypedDicts of the messages; +# - playwright/_impl/_protocol_channels.py: a typed channel class per interface. # # Usage: -# PW_SRC_DIR= python scripts/codegen/generate_protocol.py [--out ] +# PW_SRC_DIR= python scripts/codegen/generate_protocol.py \ +# --out --types-out --channels-out # # The spec is loaded the same way as utils/protocol_spec.js does it. The output # is not formatted; run black on it. import argparse import json +import keyword import os +import re import sys from pathlib import Path -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional, Set, Tuple import yaml @@ -137,6 +143,376 @@ def dump(value: Any) -> str: ) +def title_case(name: str) -> str: + return name[0].upper() + name[1:] + + +def is_optional(spec: Any) -> bool: + return (spec if isinstance(spec, str) else spec["type"]).endswith("?") + + +class TypesEmitter: + # Names follow upstream's utils/generate_channels.js. An inline object gets + # the name of its owner plus the field name. + # + # The types differ from upstream's in these ways: + # - The top-level fields of the params, results and events are all present. + # An absent optional field is None: the caller passes None, and the + # channel fills in None for a field that the driver left out. So mypy + # reports each call that does not pass a new parameter. + # - In the data that the client sends, an optional field of a nested object + # also accepts None, and an array accepts any sequence. The connection + # drops None values from objects before it sends a message. It does not + # drop them from objects inside arrays, so there an optional field does + # not accept None. + + def __init__(self, protocol: Dict[str, Any]) -> None: + self._protocol = protocol + self.blocks: List[str] = [] + self.names: Set[str] = set() + + def py_type( + self, spec: Any, name: str, sent: bool = False, in_array: bool = False + ) -> str: + if isinstance(spec, str): + base = spec.rstrip("?") + simple = { + "string": "str", + "binary": "str", + "int": "int", + "float": "float", + "boolean": "bool", + "json": "Any", + "Channel": "Channel", + } + if base in simple: + return simple[base] + if self._protocol[base]["type"] == "interface": + return f"{base}Channel" + return base + kind = spec["type"].rstrip("?") + if kind == "array": + array = "Sequence" if sent else "List" + return f"{array}[{self.py_type(spec['items'], name, sent, True)}]" + if kind == "enum": + return "Literal[" + ", ".join(json.dumps(v) for v in spec["literals"]) + "]" + self.emit_class(name, self.fields(name, spec["properties"], sent, in_array)) + return name + + def fields( + self, + name: str, + properties: Dict[str, Any], + sent: bool = False, + in_array: bool = False, + top_level: bool = False, + ) -> List[Tuple[str, str]]: + result = [] + for field, spec in properties.items(): + field_type = self.py_type(spec, name + title_case(field), sent, in_array) + if is_optional(spec): + if top_level or (sent and not in_array): + field_type = f"Optional[{field_type}]" + if not top_level: + field_type = f"NotRequired[{field_type}]" + result.append((field, field_type)) + return result + + def emit_class(self, name: str, fields: List[Tuple[str, str]]) -> None: + if name in self.names: + errors.append(f"Two types are named {name}") + self.names.add(name) + if any(keyword.iskeyword(field) for field, _ in fields): + # A field name is a Python keyword, so the class syntax cannot be used. + items = ", ".join(f'"{field}": "{t}"' for field, t in fields) + self.blocks.append(f'{name} = TypedDict("{name}", {{{items}}})') + return + body = "\n".join(f" {field}: {t}" for field, t in fields) or " pass" + self.blocks.append(f"class {name}(TypedDict):\n{body}") + + +class Payload: + """The fields of a result or an event, and the TypedDict that has them.""" + + def __init__(self, type_name: str, properties: Dict[str, Any]) -> None: + self.type_name = type_name + self.fields = [(name, is_optional(spec)) for name, spec in properties.items()] + + +class Command: + def __init__( + self, + name: str, + params: List[Tuple[str, str]], + result: Optional[Payload], + ) -> None: + self.name = name + self.params = params + self.result = result + + +class Interface: + def __init__(self, name: str, extends: Optional[str]) -> None: + self.name = name + self.extends = extends + self.commands: List[Command] = [] + self.events: List[Tuple[str, Optional[Payload]]] = [] + + +def generate_types(protocol: Dict[str, Any]) -> Tuple[str, List[Interface], Set[str]]: + converter = Converter(protocol) + emitter = TypesEmitter(protocol) + interfaces: List[Interface] = [] + aliases: List[str] = [] + channel_names: List[str] = [] + for name, item in protocol.items(): + if item["type"] == "interface": + channel_names.append(f"{name}Channel") + elif item["type"] == "enum": + emitter.names.add(name) + aliases.append( + f"{name} = {emitter.py_type(converter.type(item, name), name)}" + ) + for name, item in protocol.items(): + if item["type"] == "object": + emitter.emit_class( + name, + emitter.fields( + name, converter.properties(item.get("properties"), name) + ), + ) + if item["type"] != "interface": + continue + interface = Interface(name, item.get("extends")) + interfaces.append(interface) + if item.get("initializer"): + initializer = f"{name}Initializer" + emitter.emit_class( + initializer, + emitter.fields( + initializer, + converter.properties(item["initializer"], f"{name}.initializer"), + ), + ) + else: + # Like upstream: the initializer of the nearest ancestor that has one. + ancestor = item.get("extends") + while ancestor and not protocol[ancestor].get("initializer"): + ancestor = protocol[ancestor].get("extends") + if ancestor: + emitter.names.add(f"{name}Initializer") + emitter.blocks.append( + f"class {name}Initializer({ancestor}Initializer):\n pass" + ) + for method, command in (item.get("commands") or {}).items(): + command = command or {} + prefix = f"{name}{title_case(method)}" + # The params are the arguments of the channel method, so they have + # no TypedDict of their own. Their nested objects have one. + params = emitter.fields( + f"{prefix}Params", + converter.properties(command.get("parameters"), f"{name}.{method}"), + sent=True, + top_level=True, + ) + returns = converter.properties(command.get("returns"), f"{name}.{method}") + result = None + if returns: + result = Payload(f"{prefix}Result", returns) + emitter.emit_class( + result.type_name, + emitter.fields(result.type_name, returns, top_level=True), + ) + interface.commands.append(Command(method, params, result)) + for event, event_spec in (item.get("events") or {}).items(): + properties = converter.properties( + (event_spec or {}).get("parameters"), f"{name}.{event}" + ) + payload = None + if properties: + payload = Payload(f"{name}{title_case(event)}Event", properties) + emitter.emit_class( + payload.type_name, + emitter.fields(payload.type_name, properties, top_level=True), + ) + interface.events.append((event, payload)) + + body = "\n".join([*aliases, "", "", "\n\n\n".join(emitter.blocks)]) + used_channels = [c for c in sorted(channel_names) if re.search(rf"\b{c}\b", body)] + text = "\n".join( + [ + LICENSE, + TYPES_DOCSTRING, + "from __future__ import annotations", + "", + "from typing import (", + " TYPE_CHECKING,", + " Any,", + " List,", + " Literal,", + " Optional,", + " Sequence,", + " TypedDict,", + ")", + "", + "if TYPE_CHECKING:", + " from typing_extensions import NotRequired", + "", + " from playwright._impl._connection import Channel", + " from playwright._impl._protocol_channels import (", + *[f" {channel}," for channel in used_channels], + " )", + "", + body, + "", + ] + ) + return text, interfaces, emitter.names + + +def qualify(type_text: str, type_names: Set[str]) -> str: + """Prefixes the names from _protocol_types.py with `channels.`.""" + + def prefix(match: "re.Match[str]") -> str: + name = match.group(0) + return f"channels.{name}" if name in type_names else name + + parts = re.split(r'("[^"]*")', type_text) + for index in range(0, len(parts), 2): + parts[index] = re.sub(r"\b[A-Za-z_]\w*\b", prefix, parts[index]) + return "".join(parts) + + +def python_name(name: str) -> str: + return name + "_" if keyword.iskeyword(name) else name + + +def payload_dict(payload: Payload, source: str) -> str: + items = [] + for field, optional in payload.fields: + value = f'{source}.get("{field}")' if optional else f'{source}["{field}"]' + items.append(f'"{field}": {value}') + return "{" + ", ".join(items) + "}" + + +def generate_channels(interfaces: List[Interface], type_names: Set[str]) -> str: + blocks: List[str] = [] + for interface in interfaces: + base = f"{interface.extends}Channel" if interface.extends else "Channel" + methods: List[str] = [] + for command in interface.commands: + args = "".join( + f" {python_name(field)}: {qualify(field_type, type_names)},\n" + for field, field_type in command.params + ) + params = ( + "{" + + ", ".join( + f'"{field}": {python_name(field)}' for field, _ in command.params + ) + + "}" + ) + if command.result: + returns = f"channels.{command.result.type_name}" + body = ( + f' result = await self._call("{command.name}", {params}, timeout)\n' + f" return {payload_dict(command.result, 'result')}" + ) + else: + returns = "None" + body = f' await self._call("{command.name}", {params}, timeout)' + methods.append( + f" async def {python_name(command.name)}(\n" + f" self,\n{args} timeout: float = 0,\n" + f" ) -> {returns}:\n{body}" + ) + for event, payload in interface.events: + if payload: + handler = f"Callable[[channels.{payload.type_name}], Any]" + listener = f"lambda event: handler({payload_dict(payload, 'event')})" + else: + handler = "Callable[[], Any]" + listener = "lambda event: handler()" + suffix = title_case(event) + methods.append( + f" def on{suffix}(self, handler: {handler}) -> None:\n" + f' self._add_event_listener("{event}", handler, {listener})' + ) + methods.append( + f" def off{suffix}(self, handler: {handler}) -> None:\n" + f' self._remove_event_listener("{event}", handler)' + ) + body = "\n\n".join(methods) or " pass" + blocks.append(f"class {interface.name}Channel({base}):\n{body}") + classes = "\n".join( + f' "{interface.name}": {interface.name}Channel,' for interface in interfaces + ) + code = "\n\n\n".join(blocks) + typing_names = ["TYPE_CHECKING", "Dict", "Type"] + [ + name + for name in ["Any", "Callable", "List", "Literal", "Optional", "Sequence"] + if re.search(rf"\b{name}\[|\b{name}\b(?!\[)", code) + ] + return "\n".join( + [ + LICENSE, + CHANNELS_DOCSTRING, + "from __future__ import annotations", + "", + f"from typing import {', '.join(sorted(typing_names))}", + "", + "from playwright._impl._connection import Channel", + "", + "if TYPE_CHECKING:", + " from playwright._impl import _protocol_types as channels", + "", + "", + code, + "", + "", + "# The channel class for each protocol type name.", + "CHANNEL_CLASSES: Dict[str, Type[Channel]] = {", + classes, + "}", + "", + ] + ) + + +CHANNELS_DOCSTRING = '''"""Typed channels for the protocol interfaces, generated from packages/protocol/spec. + +Like upstream's channels in packages/protocol/src/channels.d.ts: each command is +an async method with the protocol name and parameters, and each event has +on and off methods. Every parameter must be passed; an absent +optional parameter is None. The result and the event payloads have every +top-level field; a field that the driver left out is None. `timeout` is the +timeout of the message, in milliseconds; 0 means no timeout. + +Generated by scripts/codegen/generate_protocol.py. Do not edit. +""" +''' + + +TYPES_DOCSTRING = '''"""Types of the protocol messages, generated from packages/protocol/spec. + +The names follow upstream's utils/generate_channels.js: Initializer, +Result and Event. The parameters of each +command are the arguments of its method in _protocol_channels.py. An object that +the spec writes inline is named after its owner and field, for example +PageInitializerViewportSize or BrowserNewContextParamsProxy. A reference to +another protocol object has the type Channel. + +The top-level fields of a result or an event are all present; a field that the +driver left out is None. In the data that the client sends, an optional field of +a nested object also accepts None, except in objects inside arrays, and an array +accepts any sequence. The connection drops None values from objects, but not from +objects inside arrays, before it sends a message. + +Generated by scripts/codegen/generate_protocol.py. Do not edit. +""" +''' + + LICENSE = """# Copyright (c) Microsoft Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -155,22 +531,26 @@ def dump(value: Any) -> str: def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("--out") + parser.add_argument("--out", required=True) + parser.add_argument("--types-out", required=True) + parser.add_argument("--channels-out", required=True) args = parser.parse_args() pw_src_dir = os.environ.get("PW_SRC_DIR") if not pw_src_dir: raise SystemExit( "Set PW_SRC_DIR to a microsoft/playwright checkout at the release branch in DRIVER_VERSION." ) - text = generate(load_protocol(Path(pw_src_dir) / "packages" / "protocol" / "spec")) + protocol = load_protocol(Path(pw_src_dir) / "packages" / "protocol" / "spec") + text = generate(protocol) + types_text, interfaces, type_names = generate_types(protocol) + channels_text = generate_channels(interfaces, type_names) if errors: for error in errors: print(error, file=sys.stderr) raise SystemExit(1) - if args.out: - Path(args.out).write_text(text, encoding="utf-8") - else: - sys.stdout.write(text) + Path(args.out).write_text(text, encoding="utf-8") + Path(args.types_out).write_text(types_text, encoding="utf-8") + Path(args.channels_out).write_text(channels_text, encoding="utf-8") if __name__ == "__main__": diff --git a/scripts/update_api.sh b/scripts/update_api.sh index 87ba909b8..81f3014bd 100755 --- a/scripts/update_api.sh +++ b/scripts/update_api.sh @@ -34,9 +34,9 @@ update_api structs playwright/_impl/_api_structures.py update_api sync update_api async -echo "Generating playwright/_impl/_protocol.py" -if python scripts/codegen/generate_protocol.py --out playwright/_impl/_protocol.py; then - pre-commit run --files playwright/_impl/_protocol.py +echo "Generating playwright/_impl/_protocol.py, _protocol_types.py and _protocol_channels.py" +if python scripts/codegen/generate_protocol.py --out playwright/_impl/_protocol.py --types-out playwright/_impl/_protocol_types.py --channels-out playwright/_impl/_protocol_channels.py; then + pre-commit run --files playwright/_impl/_protocol.py playwright/_impl/_protocol_types.py playwright/_impl/_protocol_channels.py else echo "Exited due to errors" exit 1 diff --git a/tests/async/test_route_web_socket.py b/tests/async/test_route_web_socket.py index 823695394..7a7c266d7 100644 --- a/tests/async/test_route_web_socket.py +++ b/tests/async/test_route_web_socket.py @@ -321,6 +321,34 @@ def _ws_on_message(message: Union[str, bytes]) -> None: ) +async def test_should_emit_close_upon_frame_navigation( + page: Page, server: Server +) -> None: + future: asyncio.Future[WebSocketRoute] = asyncio.Future() + + async def _handle_ws(ws: WebSocketRoute) -> None: + ws.connect_to_server() + future.set_result(ws) + + await page.route_web_socket(re.compile(".*"), _handle_ws) + await setup_ws(page, server, "blob") + + route = await future + route.send("hello") + await assert_equal( + lambda: page.evaluate("window.log"), + [ + "open", + f"message: data=hello origin=ws://localhost:{server.PORT} lastEventId=", + ], + ) + + closed: asyncio.Future[None] = asyncio.Future() + route.on_close(lambda code, reason: closed.set_result(None)) + await page.goto(server.EMPTY_PAGE) + await closed + + async def test_should_work_with_base_url(browser: Browser, server: Server) -> None: context = await browser.new_context(base_url=f"http://localhost:{server.PORT}") page = await context.new_page() diff --git a/tests/sync/test_route_web_socket.py b/tests/sync/test_route_web_socket.py index 6d9eb09a6..5bcd921b8 100644 --- a/tests/sync/test_route_web_socket.py +++ b/tests/sync/test_route_web_socket.py @@ -316,6 +316,35 @@ def _ws_on_message(message: Union[str, bytes]) -> None: ) +def test_should_emit_close_upon_frame_navigation(page: Page, server: Server) -> None: + route: Optional["WebSocketRoute"] = None + + def _handle_ws(ws: WebSocketRoute) -> None: + ws.connect_to_server() + nonlocal route + route = ws + + page.route_web_socket(re.compile(".*"), _handle_ws) + setup_ws(page, server, "blob") + page.evaluate("window.wsOpened") + + assert route + route.send("hello") + assert_equal( + lambda: page.evaluate("window.log"), + [ + "open", + f"message: data=hello origin=ws://localhost:{server.PORT} lastEventId=", + ], + ) + + closed = [] + route.on_close(lambda code, reason: closed.append((code, reason))) + page.goto(server.EMPTY_PAGE) + # page.evaluate lets the close event arrive. + assert_equal(lambda: page.evaluate("1") and len(closed), 1) + + def test_should_work_with_base_url(browser: Browser, server: Server) -> None: context = browser.new_context(base_url=f"http://localhost:{server.PORT}") page = context.new_page()