From eaf3f155285ef567df10b90bae92a0a68557d47e Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Thu, 1 Dec 2022 16:46:12 -0800 Subject: [PATCH] chore: lint examples and fix to_{have,contain}_text with lists (#1675) --- .pre-commit-config.yaml | 1 - README.md | 2 +- .../todomvc/{tests => mvctests}/__init__.py | 0 .../test_clear_completed_button.py | 0 .../{tests => mvctests}/test_counter.py | 0 .../{tests => mvctests}/test_editing.py | 0 .../todomvc/{tests => mvctests}/test_item.py | 0 .../test_mark_all_as_completed.py | 0 .../{tests => mvctests}/test_new_todo.py | 0 .../{tests => mvctests}/test_persistence.py | 0 .../{tests => mvctests}/test_routing.py | 0 examples/todomvc/{tests => mvctests}/utils.py | 4 +- playwright/_impl/_assertions.py | 52 +- playwright/async_api/_generated.py | 5609 ++++++++++------ playwright/sync_api/_generated.py | 5663 +++++++++++------ pyproject.toml | 1 - setup.py | 2 +- tests/server.py | 12 +- tests/sync/test_assertions.py | 8 +- .../test_browsercontext_request_fallback.py | 81 +- tests/sync/test_page_request_fallback.py | 82 +- 21 files changed, 7389 insertions(+), 4128 deletions(-) rename examples/todomvc/{tests => mvctests}/__init__.py (100%) rename examples/todomvc/{tests => mvctests}/test_clear_completed_button.py (100%) rename examples/todomvc/{tests => mvctests}/test_counter.py (100%) rename examples/todomvc/{tests => mvctests}/test_editing.py (100%) rename examples/todomvc/{tests => mvctests}/test_item.py (100%) rename examples/todomvc/{tests => mvctests}/test_mark_all_as_completed.py (100%) rename examples/todomvc/{tests => mvctests}/test_new_todo.py (100%) rename examples/todomvc/{tests => mvctests}/test_persistence.py (100%) rename examples/todomvc/{tests => mvctests}/test_routing.py (100%) rename examples/todomvc/{tests => mvctests}/utils.py (87%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9ed64323..6a46b88cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,6 @@ repos: hooks: - id: mypy additional_dependencies: [types-pyOpenSSL==22.1.0.1] - exclude: examples/ - repo: https://github.com/pycqa/flake8 rev: 5.0.4 hooks: diff --git a/README.md b/README.md index e1413a947..04c25a7b1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 108.0.5359.29 | ✅ | ✅ | ✅ | +| Chromium 108.0.5359.48 | ✅ | ✅ | ✅ | | WebKit 16.4 | ✅ | ✅ | ✅ | | Firefox 106.0 | ✅ | ✅ | ✅ | diff --git a/examples/todomvc/tests/__init__.py b/examples/todomvc/mvctests/__init__.py similarity index 100% rename from examples/todomvc/tests/__init__.py rename to examples/todomvc/mvctests/__init__.py diff --git a/examples/todomvc/tests/test_clear_completed_button.py b/examples/todomvc/mvctests/test_clear_completed_button.py similarity index 100% rename from examples/todomvc/tests/test_clear_completed_button.py rename to examples/todomvc/mvctests/test_clear_completed_button.py diff --git a/examples/todomvc/tests/test_counter.py b/examples/todomvc/mvctests/test_counter.py similarity index 100% rename from examples/todomvc/tests/test_counter.py rename to examples/todomvc/mvctests/test_counter.py diff --git a/examples/todomvc/tests/test_editing.py b/examples/todomvc/mvctests/test_editing.py similarity index 100% rename from examples/todomvc/tests/test_editing.py rename to examples/todomvc/mvctests/test_editing.py diff --git a/examples/todomvc/tests/test_item.py b/examples/todomvc/mvctests/test_item.py similarity index 100% rename from examples/todomvc/tests/test_item.py rename to examples/todomvc/mvctests/test_item.py diff --git a/examples/todomvc/tests/test_mark_all_as_completed.py b/examples/todomvc/mvctests/test_mark_all_as_completed.py similarity index 100% rename from examples/todomvc/tests/test_mark_all_as_completed.py rename to examples/todomvc/mvctests/test_mark_all_as_completed.py diff --git a/examples/todomvc/tests/test_new_todo.py b/examples/todomvc/mvctests/test_new_todo.py similarity index 100% rename from examples/todomvc/tests/test_new_todo.py rename to examples/todomvc/mvctests/test_new_todo.py diff --git a/examples/todomvc/tests/test_persistence.py b/examples/todomvc/mvctests/test_persistence.py similarity index 100% rename from examples/todomvc/tests/test_persistence.py rename to examples/todomvc/mvctests/test_persistence.py diff --git a/examples/todomvc/tests/test_routing.py b/examples/todomvc/mvctests/test_routing.py similarity index 100% rename from examples/todomvc/tests/test_routing.py rename to examples/todomvc/mvctests/test_routing.py diff --git a/examples/todomvc/tests/utils.py b/examples/todomvc/mvctests/utils.py similarity index 87% rename from examples/todomvc/tests/utils.py rename to examples/todomvc/mvctests/utils.py index 635845a4f..ba3d5decb 100644 --- a/examples/todomvc/tests/utils.py +++ b/examples/todomvc/mvctests/utils.py @@ -23,4 +23,6 @@ def assert_number_of_todos_in_local_storage(page: Page, expected: int) -> None: def check_todos_in_local_storage(page: Page, title: str) -> None: - title in page.evaluate("JSON.parse(localStorage['react-todos']).map(i => i.title)") + assert title in page.evaluate( + "JSON.parse(localStorage['react-todos']).map(i => i.title)" + ) diff --git a/playwright/_impl/_assertions.py b/playwright/_impl/_assertions.py index 3068a313c..2253bdabd 100644 --- a/playwright/_impl/_assertions.py +++ b/playwright/_impl/_assertions.py @@ -120,7 +120,13 @@ def _not(self) -> "LocatorAssertions": async def to_contain_text( self, - expected: Union[List[Union[Pattern[str], str]], Pattern[str], str], + expected: Union[ + List[str], + List[Pattern[str]], + List[Union[Pattern[str], str]], + Pattern[str], + str, + ], use_inner_text: bool = None, timeout: float = None, ignore_case: bool = None, @@ -163,7 +169,13 @@ async def to_contain_text( async def not_to_contain_text( self, - expected: Union[List[Union[Pattern[str], str]], Pattern[str], str], + expected: Union[ + List[str], + List[Pattern[str]], + List[Union[Pattern[str], str]], + Pattern[str], + str, + ], use_inner_text: bool = None, timeout: float = None, ignore_case: bool = None, @@ -199,7 +211,13 @@ async def not_to_have_attribute( async def to_have_class( self, - expected: Union[List[Union[Pattern[str], str]], Pattern[str], str], + expected: Union[ + List[str], + List[Pattern[str]], + List[Union[Pattern[str], str]], + Pattern[str], + str, + ], timeout: float = None, ) -> None: __tracebackhide__ = True @@ -222,7 +240,13 @@ async def to_have_class( async def not_to_have_class( self, - expected: Union[List[Union[Pattern[str], str]], Pattern[str], str], + expected: Union[ + List[str], + List[Pattern[str]], + List[Union[Pattern[str], str]], + Pattern[str], + str, + ], timeout: float = None, ) -> None: __tracebackhide__ = True @@ -346,7 +370,7 @@ async def not_to_have_value( async def to_have_values( self, - values: List[Union[Pattern[str], str]], + values: Union[List[str], List[Pattern[str]], List[Union[Pattern[str], str]]], timeout: float = None, ) -> None: __tracebackhide__ = True @@ -360,7 +384,7 @@ async def to_have_values( async def not_to_have_values( self, - values: List[Union[Pattern[str], str]], + values: Union[List[str], List[Pattern[str]], List[Union[Pattern[str], str]]], timeout: float = None, ) -> None: __tracebackhide__ = True @@ -368,7 +392,13 @@ async def not_to_have_values( async def to_have_text( self, - expected: Union[List[Union[Pattern[str], str]], Pattern[str], str], + expected: Union[ + List[str], + List[Pattern[str]], + List[Union[Pattern[str], str]], + Pattern[str], + str, + ], use_inner_text: bool = None, timeout: float = None, ignore_case: bool = None, @@ -407,7 +437,13 @@ async def to_have_text( async def not_to_have_text( self, - expected: Union[List[Union[Pattern[str], str]], Pattern[str], str], + expected: Union[ + List[str], + List[Pattern[str]], + List[Union[Pattern[str], str]], + Pattern[str], + str, + ], use_inner_text: bool = None, timeout: float = None, ignore_case: bool = None, diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index cc0bb862c..499701797 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -103,8 +103,8 @@ def resource_type(self) -> str: """Request.resource_type Contains the request's resource type as it was perceived by the rendering engine. ResourceType will be one of the - following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, - `websocket`, `manifest`, `other`. + following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, + `eventsource`, `websocket`, `manifest`, `other`. Returns ------- @@ -181,10 +181,12 @@ def redirected_from(self) -> typing.Optional["Request"]: Request that was redirected by the server to this one, if any. - When the server responds with a redirect, Playwright creates a new `Request` object. The two requests are connected by - `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is possible to + When the server responds with a redirect, Playwright creates a new `Request` object. The two requests are connected + by `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is possible to construct the whole redirect chain by repeatedly calling `redirectedFrom()`. + **Usage** + For example, if the website `http://example.com` redirects to `https://example.com`: ```py @@ -192,6 +194,11 @@ def redirected_from(self) -> typing.Optional["Request"]: print(response.request.redirected_from.url) # \"http://example.com\" ``` + ```py + response = page.goto(\"http://example.com\") + print(response.request.redirected_from.url) # \"http://example.com\" + ``` + If the website `https://google.com` has no redirects: ```py @@ -199,6 +206,11 @@ def redirected_from(self) -> typing.Optional["Request"]: print(response.request.redirected_from) # None ``` + ```py + response = page.goto(\"https://google.com\") + print(response.request.redirected_from) # None + ``` + Returns ------- Union[Request, None] @@ -211,6 +223,8 @@ def redirected_to(self) -> typing.Optional["Request"]: New request issued by the browser if the server responded with redirect. + **Usage** + This method is the opposite of `request.redirected_from()`: ```py @@ -229,6 +243,8 @@ def failure(self) -> typing.Optional[str]: The method returns `null` unless this request has failed, as reported by `requestfailed` event. + **Usage** + Example of logging of all the failed requests: ```py @@ -245,10 +261,12 @@ def failure(self) -> typing.Optional[str]: def timing(self) -> ResourceTiming: """Request.timing - Returns resource timing information for given request. Most of the timing values become available upon the response, - `responseEnd` becomes available when request finishes. Find more information at + Returns resource timing information for given request. Most of the timing values become available upon the + response, `responseEnd` becomes available when request finishes. Find more information at [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming). + **Usage** + ```py async with page.expect_event(\"requestfinished\") as request_info: await page.goto(\"http://example.com\") @@ -256,6 +274,13 @@ def timing(self) -> ResourceTiming: print(request.timing) ``` + ```py + with page.expect_event(\"requestfinished\") as request_info: + page.goto(\"http://example.com\") + request = request_info.value + print(request.timing) + ``` + Returns ------- {startTime: float, domainLookupStart: float, domainLookupEnd: float, connectStart: float, secureConnectionStart: float, connectEnd: float, requestStart: float, responseStart: float, responseEnd: float} @@ -267,8 +292,8 @@ def headers(self) -> typing.Dict[str, str]: """Request.headers An object with the request HTTP headers. The header names are lower-cased. Note that this method does not return - security-related headers, including cookie-related ones. You can use `request.all_headers()` for complete list of - headers that include `cookie` information. + security-related headers, including cookie-related ones. You can use `request.all_headers()` for complete + list of headers that include `cookie` information. Returns ------- @@ -327,8 +352,9 @@ async def all_headers(self) -> typing.Dict[str, str]: async def headers_array(self) -> typing.List[NameValue]: """Request.headers_array - An array with all the request HTTP headers associated with this request. Unlike `request.all_headers()`, header - names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times. + An array with all the request HTTP headers associated with this request. Unlike `request.all_headers()`, + header names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple + times. Returns ------- @@ -412,8 +438,8 @@ def headers(self) -> typing.Dict[str, str]: """Response.headers An object with the response HTTP headers. The header names are lower-cased. Note that this method does not return - security-related headers, including cookie-related ones. You can use `response.all_headers()` for complete list - of headers that include `cookie` information. + security-related headers, including cookie-related ones. You can use `response.all_headers()` for complete + list of headers that include `cookie` information. Returns ------- @@ -473,8 +499,9 @@ async def all_headers(self) -> typing.Dict[str, str]: async def headers_array(self) -> typing.List[NameValue]: """Response.headers_array - An array with all the request HTTP headers associated with this response. Unlike `response.all_headers()`, header - names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times. + An array with all the request HTTP headers associated with this response. Unlike `response.all_headers()`, + header names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple + times. Returns ------- @@ -486,9 +513,9 @@ async def headers_array(self) -> typing.List[NameValue]: async def header_value(self, name: str) -> typing.Optional[str]: """Response.header_value - Returns the value of the header matching the name. The name is case insensitive. If multiple headers have the same name - (except `set-cookie`), they are returned as a list separated by `, `. For `set-cookie`, the `\\n` separator is used. If - no headers are found, `null` is returned. + Returns the value of the header matching the name. The name is case insensitive. If multiple headers have the same + name (except `set-cookie`), they are returned as a list separated by `, `. For `set-cookie`, the `\\n` separator is + used. If no headers are found, `null` is returned. Parameters ---------- @@ -617,11 +644,11 @@ async def abort(self, error_code: typing.Optional[str] = None) -> None: Optional error code. Defaults to `failed`, could be one of the following: - `'aborted'` - An operation was aborted (due to user action) - `'accessdenied'` - Permission to access a resource, other than the network, was denied - - `'addressunreachable'` - The IP address is unreachable. This usually means that there is no route to the specified - host or network. + - `'addressunreachable'` - The IP address is unreachable. This usually means that there is no route to the + specified host or network. - `'blockedbyclient'` - The client chose to block the request. - - `'blockedbyresponse'` - The request failed because the response was delivered along with requirements which are not - met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance). + - `'blockedbyresponse'` - The request failed because the response was delivered along with requirements which are + not met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance). - `'connectionaborted'` - A connection timed out as a result of not receiving an ACK for data sent. - `'connectionclosed'` - A connection was closed (corresponding to a TCP FIN). - `'connectionfailed'` - A connection attempt failed. @@ -649,6 +676,8 @@ async def fulfill( Fulfills route's request with given response. + **Usage** + An example of fulfilling all requests with 404 responses: ```py @@ -658,12 +687,23 @@ async def fulfill( body=\"not found!\")) ``` + ```py + page.route(\"**/*\", lambda route: route.fulfill( + status=404, + content_type=\"text/plain\", + body=\"not found!\")) + ``` + An example of serving static file: ```py await page.route(\"**/xhr_endpoint\", lambda route: route.fulfill(path=\"mock_data.json\")) ``` + ```py + page.route(\"**/xhr_endpoint\", lambda route: route.fulfill(path=\"mock_data.json\")) + ``` + Parameters ---------- status : Union[int, None] @@ -673,13 +713,13 @@ async def fulfill( body : Union[bytes, str, None] Response body. path : Union[pathlib.Path, str, None] - File path to respond with. The content type will be inferred from file extension. If `path` is a relative path, then it - is resolved relative to the current working directory. + File path to respond with. The content type will be inferred from file extension. If `path` is a relative path, + then it is resolved relative to the current working directory. content_type : Union[str, None] If set, equals to setting `Content-Type` response header. response : Union[APIResponse, None] - `APIResponse` to fulfill route's request with. Individual fields of the response (such as headers) can be overridden - using fulfill options. + `APIResponse` to fulfill route's request with. Individual fields of the response (such as headers) can be + overridden using fulfill options. """ return mapping.from_maybe_impl( @@ -703,19 +743,27 @@ async def fallback( ) -> None: """Route.fallback - When several routes match the given pattern, they run in the order opposite to their registration. That way the last - registered route can always override all the previous ones. In the example below, request will be handled by the - bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first + When several routes match the given pattern, they run in the order opposite to their registration. That way the + last registered route can always override all the previous ones. In the example below, request will be handled by + the bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first registered route. + **Usage** + ```py await page.route(\"**/*\", lambda route: route.abort()) # Runs last. await page.route(\"**/*\", lambda route: route.fallback()) # Runs second. await page.route(\"**/*\", lambda route: route.fallback()) # Runs first. ``` - Registering multiple routes is useful when you want separate handlers to handle different kinds of requests, for example - API calls vs page resources or GET requests vs POST requests as in the example below. + ```py + page.route(\"**/*\", lambda route: route.abort()) # Runs last. + page.route(\"**/*\", lambda route: route.fallback()) # Runs second. + page.route(\"**/*\", lambda route: route.fallback()) # Runs first. + ``` + + Registering multiple routes is useful when you want separate handlers to handle different kinds of requests, for + example API calls vs page resources or GET requests vs POST requests as in the example below. ```py # Handle GET requests. @@ -738,8 +786,29 @@ def handle_post(route): await page.route(\"**/*\", handle_post) ``` - One can also modify request while falling back to the subsequent handler, that way intermediate route handler can modify - url, method, headers and postData of the request. + ```py + # Handle GET requests. + def handle_post(route): + if route.request.method != \"GET\": + route.fallback() + return + # Handling GET only. + # ... + + # Handle POST requests. + def handle_post(route): + if route.request.method != \"POST\": + route.fallback() + return + # Handling POST only. + # ... + + page.route(\"**/*\", handle_get) + page.route(\"**/*\", handle_post) + ``` + + One can also modify request while falling back to the subsequent handler, that way intermediate route handler can + modify url, method, headers and postData of the request. ```py async def handle(route, request): @@ -754,11 +823,24 @@ async def handle(route, request): await page.route(\"**/*\", handle) ``` + ```py + def handle(route, request): + # override headers + headers = { + **request.headers, + \"foo\": \"foo-value\" # set \"foo\" header + \"bar\": None # remove \"bar\" header + } + route.fallback(headers=headers) + } + page.route(\"**/*\", handle) + ``` + Parameters ---------- url : Union[str, None] - If set changes the request URL. New URL must have same protocol as original one. Changing the URL won't affect the route - matching, all the routes are matched using the original request URL. + If set changes the request URL. New URL must have same protocol as original one. Changing the URL won't affect the + route matching, all the routes are matched using the original request URL. method : Union[str, None] If set changes the request method (e.g. GET or POST) headers : Union[Dict[str, str], None] @@ -788,6 +870,8 @@ async def continue_( Continues route's request with optional overrides. + **Usage** + ```py async def handle(route, request): # override headers @@ -801,6 +885,19 @@ async def handle(route, request): await page.route(\"**/*\", handle) ``` + ```py + def handle(route, request): + # override headers + headers = { + **request.headers, + \"foo\": \"foo-value\" # set \"foo\" header + \"bar\": None # remove \"bar\" header + } + route.continue_(headers=headers) + } + page.route(\"**/*\", handle) + ``` + Parameters ---------- url : Union[str, None] @@ -952,8 +1049,8 @@ def expect_event( predicate : Union[Callable, None] Receives the event data and resolves to truthy value when the waiting should resolve. timeout : Union[float, None] - Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default - value can be changed by using the `browser_context.set_default_timeout()`. + Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The + default value can be changed by using the `browser_context.set_default_timeout()`. Returns ------- @@ -975,11 +1072,11 @@ async def wait_for_event( ) -> typing.Any: """WebSocket.wait_for_event - > NOTE: In most cases, you should use `web_socket.expect_event()`. + **NOTE** In most cases, you should use `web_socket.expect_event()`. - Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function and - waits for `predicate(event)` to return a truthy value. Will throw an error if the socket is closed before the `event` is - fired. + Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function + and waits for `predicate(event)` to return a truthy value. Will throw an error if the socket is closed before the + `event` is fired. Parameters ---------- @@ -988,8 +1085,8 @@ async def wait_for_event( predicate : Union[Callable, None] Receives the event data and resolves to truthy value when the waiting should resolve. timeout : Union[float, None] - Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default - value can be changed by using the `browser_context.set_default_timeout()`. + Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The + default value can be changed by using the `browser_context.set_default_timeout()`. Returns ------- @@ -1024,12 +1121,14 @@ async def down(self, key: str) -> None: Dispatches a `keydown` event. - `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) - value or a single character to generate the text for. A superset of the `key` values can be found + `key` can specify the intended + [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character + to generate the text for. A superset of the `key` values can be found [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, - `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. + `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, + etc. Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. @@ -1038,14 +1137,14 @@ async def down(self, key: str) -> None: If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective texts. - If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that modifier - active. To release the modifier key, use `keyboard.up()`. + If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that + modifier active. To release the modifier key, use `keyboard.up()`. After the key is pressed once, subsequent calls to `keyboard.down()` will have - [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key, use - `keyboard.up()`. + [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key, + use `keyboard.up()`. - > NOTE: Modifier keys DO influence `keyboard.down`. Holding down `Shift` will type the text in upper case. + **NOTE** Modifier keys DO influence `keyboard.down`. Holding down `Shift` will type the text in upper case. Parameters ---------- @@ -1073,11 +1172,18 @@ async def insert_text(self, text: str) -> None: Dispatches only `input` event, does not emit the `keydown`, `keyup` or `keypress` events. + **Usage** + ```py await page.keyboard.insert_text(\"嗨\") ``` - > NOTE: Modifier keys DO NOT effect `keyboard.insertText`. Holding down `Shift` will not type the text in upper case. + ```py + page.keyboard.insert_text(\"嗨\") + ``` + + **NOTE** Modifier keys DO NOT effect `keyboard.insertText`. Holding down `Shift` will not type the text in upper + case. Parameters ---------- @@ -1094,13 +1200,21 @@ async def type(self, text: str, *, delay: typing.Optional[float] = None) -> None To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`. + **Usage** + ```py await page.keyboard.type(\"Hello\") # types instantly await page.keyboard.type(\"World\", delay=100) # types slower, like a user ``` - > NOTE: Modifier keys DO NOT effect `keyboard.type`. Holding down `Shift` will not type the text in upper case. - > NOTE: For characters that are not on a US keyboard, only an `input` event will be sent. + ```py + page.keyboard.type(\"Hello\") # types instantly + page.keyboard.type(\"World\", delay=100) # types slower, like a user + ``` + + **NOTE** Modifier keys DO NOT effect `keyboard.type`. Holding down `Shift` will not type the text in upper case. + + **NOTE** For characters that are not on a US keyboard, only an `input` event will be sent. Parameters ---------- @@ -1117,12 +1231,14 @@ async def type(self, text: str, *, delay: typing.Optional[float] = None) -> None async def press(self, key: str, *, delay: typing.Optional[float] = None) -> None: """Keyboard.press - `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) - value or a single character to generate the text for. A superset of the `key` values can be found + `key` can specify the intended + [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character + to generate the text for. A superset of the `key` values can be found [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, - `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. + `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, + etc. Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. @@ -1134,6 +1250,8 @@ async def press(self, key: str, *, delay: typing.Optional[float] = None) -> None Shortcuts such as `key: \"Control+o\"` or `key: \"Control+Shift+T\"` are supported as well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed. + **Usage** + ```py page = await browser.new_page() await page.goto(\"https://keycode.info\") @@ -1146,6 +1264,18 @@ async def press(self, key: str, *, delay: typing.Optional[float] = None) -> None await browser.close() ``` + ```py + page = browser.new_page() + page.goto(\"https://keycode.info\") + page.keyboard.press(\"a\") + page.screenshot(path=\"a.png\") + page.keyboard.press(\"ArrowLeft\") + page.screenshot(path=\"arrow_left.png\") + page.keyboard.press(\"Shift+O\") + page.screenshot(path=\"o.png\") + browser.close() + ``` + Shortcut for `keyboard.down()` and `keyboard.up()`. Parameters @@ -1287,8 +1417,8 @@ async def wheel(self, delta_x: float, delta_y: float) -> None: Dispatches a `wheel` event. - > NOTE: Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling to - finish before returning. + **NOTE** Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling + to finish before returning. Parameters ---------- @@ -1334,20 +1464,26 @@ async def evaluate( This method passes this handle as the first argument to `expression`. - If `expression` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its value. + If `expression` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its + value. - Examples: + **Usage** ```py tweet_handle = await page.query_selector(\".tweet .retweets\") assert await tweet_handle.evaluate(\"node => node.innerText\") == \"10 retweets\" ``` + ```py + tweet_handle = page.query_selector(\".tweet .retweets\") + assert tweet_handle.evaluate(\"node => node.innerText\") == \"10 retweets\" + ``` + Parameters ---------- expression : str - JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is - automatically invoked. + JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the + function is automatically invoked. arg : Union[Any, None] Optional argument to pass to `expression`. @@ -1371,19 +1507,19 @@ async def evaluate_handle( This method passes this handle as the first argument to `expression`. - The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` returns - `JSHandle`. + The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` + returns `JSHandle`. - If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would wait - for the promise to resolve and return its value. + If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would + wait for the promise to resolve and return its value. See `page.evaluate_handle()` for more details. Parameters ---------- expression : str - JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is - automatically invoked. + JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the + function is automatically invoked. arg : Union[Any, None] Optional argument to pass to `expression`. @@ -1422,6 +1558,8 @@ async def get_properties(self) -> typing.Dict[str, "JSHandle"]: The method returns a map with **own property names** as keys and JSHandle instances for the property values. + **Usage** + ```py handle = await page.evaluate_handle(\"({window, document})\") properties = await handle.get_properties() @@ -1430,6 +1568,14 @@ async def get_properties(self) -> typing.Dict[str, "JSHandle"]: await handle.dispose() ``` + ```py + handle = page.evaluate_handle(\"({window, document})\") + properties = handle.get_properties() + window_handle = properties.get(\"window\") + document_handle = properties.get(\"document\") + handle.dispose() + ``` + Returns ------- Dict[str, JSHandle] @@ -1462,8 +1608,8 @@ async def json_value(self) -> typing.Any: Returns a JSON representation of the object. If the object has a `toJSON` function, it **will not be called**. - > NOTE: The method will return an empty JSON object if the referenced object is not stringifiable. It will throw an - error if the object has circular references. + **NOTE** The method will return an empty JSON object if the referenced object is not stringifiable. It will throw + an error if the object has circular references. Returns ------- @@ -1647,12 +1793,18 @@ async def dispatch_event( `click` is dispatched. This is equivalent to calling [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click). + **Usage** + ```py await element_handle.dispatch_event(\"click\") ``` - Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` properties - and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. + ```py + element_handle.dispatch_event(\"click\") + ``` + + Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` + properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties: - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent) @@ -1671,6 +1823,12 @@ async def dispatch_event( await element_handle.dispatch_event(\"#source\", \"dragstart\", {\"dataTransfer\": data_transfer}) ``` + ```py + # note you can only create data_transfer in chromium and firefox + data_transfer = page.evaluate_handle(\"new DataTransfer()\") + element_handle.dispatch_event(\"#source\", \"dragstart\", {\"dataTransfer\": data_transfer}) + ``` + Parameters ---------- type : str @@ -1690,8 +1848,8 @@ async def scroll_into_view_if_needed( ) -> None: """ElementHandle.scroll_into_view_if_needed - This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then tries to scroll element into view, unless it is - completely visible as defined by + This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then tries to scroll element into view, unless + it is completely visible as defined by [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`. Throws when `elementHandle` does not point to an element @@ -1700,8 +1858,8 @@ async def scroll_into_view_if_needed( Parameters ---------- timeout : Union[float, None] - Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by - using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. + Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed + by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. """ return mapping.from_maybe_impl( @@ -1730,29 +1888,29 @@ async def hover( If the element is detached from the DOM at any moment during the action, this method throws. - When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing - zero timeout disables this. + When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. + Passing zero timeout disables this. Parameters ---------- modifiers : Union[List[Union["Alt", "Control", "Meta", "Shift"]], None] - Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current - modifiers back. If not specified, currently pressed modifiers are used. + Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores + current modifiers back. If not specified, currently pressed modifiers are used. position : Union[{x: float, y: float}, None] - A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the - element. + A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of + the element. timeout : Union[float, None] - Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by - using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. + Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed + by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. no_wait_after : Union[bool, None] - Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can - opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to - inaccessible pages. Defaults to `false`. + Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You + can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as + navigating to inaccessible pages. Defaults to `false`. force : Union[bool, None] Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`. trial : Union[bool, None] - When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults to - `false`. Useful to wait until the element is ready for the action without performing it. + When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults + to `false`. Useful to wait until the element is ready for the action without performing it. """ return mapping.from_maybe_impl( @@ -1791,17 +1949,17 @@ async def click( If the element is detached from the DOM at any moment during the action, this method throws. - When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing - zero timeout disables this. + When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. + Passing zero timeout disables this. Parameters ---------- modifiers : Union[List[Union["Alt", "Control", "Meta", "Shift"]], None] - Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current - modifiers back. If not specified, currently pressed modifiers are used. + Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores + current modifiers back. If not specified, currently pressed modifiers are used. position : Union[{x: float, y: float}, None] - A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the - element. + A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of + the element. delay : Union[float, None] Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0. button : Union["left", "middle", "right", None] @@ -1809,17 +1967,17 @@ async def click( click_count : Union[int, None] defaults to 1. See [UIEvent.detail]. timeout : Union[float, None] - Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by - using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. + Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed + by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. force : Union[bool, None] Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`. no_wait_after : Union[bool, None] - Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can - opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to - inaccessible pages. Defaults to `false`. + Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You + can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as + navigating to inaccessible pages. Defaults to `false`. trial : Union[bool, None] - When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults to - `false`. Useful to wait until the element is ready for the action without performing it. + When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults + to `false`. Useful to wait until the element is ready for the action without performing it. """ return mapping.from_maybe_impl( @@ -1856,40 +2014,40 @@ async def dblclick( 1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set. 1. Scroll the element into view if needed. 1. Use `page.mouse` to double click in the center of the element, or the specified `position`. - 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. Note that if the - first click of the `dblclick()` triggers a navigation event, this method will throw. + 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. Note that if + the first click of the `dblclick()` triggers a navigation event, this method will throw. If the element is detached from the DOM at any moment during the action, this method throws. - When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing - zero timeout disables this. + When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. + Passing zero timeout disables this. - > NOTE: `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event. + **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event. Parameters ---------- modifiers : Union[List[Union["Alt", "Control", "Meta", "Shift"]], None] - Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current - modifiers back. If not specified, currently pressed modifiers are used. + Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores + current modifiers back. If not specified, currently pressed modifiers are used. position : Union[{x: float, y: float}, None] - A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the - element. + A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of + the element. delay : Union[float, None] Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0. button : Union["left", "middle", "right", None] Defaults to `left`. timeout : Union[float, None] - Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by - using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. + Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed + by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods. force : Union[bool, None] Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`. no_wait_after : Union[bool, None] - Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can - opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to - inaccessible pages. Defaults to `false`. + Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You + can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as + navigating to inaccessible pages. Defaults to `false`. trial : Union[bool, None] - When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults to - `false`. Useful to wait until the element is ready for the action without performing it. + When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults + to `false`. Useful to wait until the element is ready for the action without performing it. """ return mapping.from_maybe_impl( @@ -1920,17 +2078,20 @@ async def select_option( ) -> typing.List[str]: """ElementHandle.select_option - This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits until all specified options are present in the - `` element and selects these options. - If the target element is not a `` element, this method throws an error. However, if the element is inside + the `