From fb5dea41a327bd4512240528640e142f4e1a2e2f Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Mon, 9 Sep 2024 13:23:36 +0200 Subject: [PATCH 1/2] chore(roll): roll Playwright to v1.47.0 --- README.md | 4 +- playwright/_impl/_api_structures.py | 3 + playwright/_impl/_element_handle.py | 1 - playwright/_impl/_fetch.py | 22 +++- playwright/_impl/_frame.py | 1 - playwright/_impl/_network.py | 6 + playwright/async_api/_generated.py | 122 +++++++++--------- playwright/sync_api/_generated.py | 122 +++++++++--------- setup.py | 2 +- ...test_browsercontext_client_certificates.py | 41 ++++++ tests/async/test_fetch_browser_context.py | 44 ++++++- ...test_browsercontext_client_certificates.py | 38 ++++++ tests/sync/test_fetch_browser_context.py | 39 +++++- 13 files changed, 306 insertions(+), 139 deletions(-) diff --git a/README.md b/README.md index 9de46843e..d94692919 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 128.0.6613.18 | ✅ | ✅ | ✅ | +| Chromium 129.0.6668.29 | ✅ | ✅ | ✅ | | WebKit 18.0 | ✅ | ✅ | ✅ | -| Firefox 128.0 | ✅ | ✅ | ✅ | +| Firefox 130.0 | ✅ | ✅ | ✅ | ## Documentation diff --git a/playwright/_impl/_api_structures.py b/playwright/_impl/_api_structures.py index 34cfc8a48..904a590a9 100644 --- a/playwright/_impl/_api_structures.py +++ b/playwright/_impl/_api_structures.py @@ -104,8 +104,11 @@ class StorageState(TypedDict, total=False): class ClientCertificate(TypedDict, total=False): origin: str certPath: Optional[Union[str, Path]] + cert: Optional[bytes] keyPath: Optional[Union[str, Path]] + key: Optional[bytes] pfxPath: Optional[Union[str, Path]] + pfx: Optional[bytes] passphrase: Optional[str] diff --git a/playwright/_impl/_element_handle.py b/playwright/_impl/_element_handle.py index 39e43a6fd..d7482fdea 100644 --- a/playwright/_impl/_element_handle.py +++ b/playwright/_impl/_element_handle.py @@ -157,7 +157,6 @@ async def select_option( params = locals_to_params( dict( timeout=timeout, - noWaitAfter=noWaitAfter, force=force, **convert_select_option_values(value, index, label, element) ) diff --git a/playwright/_impl/_fetch.py b/playwright/_impl/_fetch.py index 8dde5a541..a4de751bd 100644 --- a/playwright/_impl/_fetch.py +++ b/playwright/_impl/_fetch.py @@ -18,6 +18,7 @@ import typing from pathlib import Path from typing import Any, Dict, List, Optional, Union, cast +from urllib.parse import parse_qs import playwright._impl._network as network from playwright._impl._api_structures import ( @@ -53,7 +54,7 @@ FormType = Dict[str, Union[bool, float, str]] DataType = Union[Any, bytes, str] MultipartType = Dict[str, Union[bytes, bool, float, str, FilePayload]] -ParamsType = Dict[str, Union[bool, float, str]] +ParamsType = Union[Dict[str, Union[bool, float, str]], str] class APIRequest: @@ -404,7 +405,7 @@ async def _inner_fetch( "fetch", { "url": url, - "params": object_to_array(params), + "params": params_to_protocol(params), "method": method, "headers": serialized_headers, "postData": post_data, @@ -429,6 +430,23 @@ async def storage_state( return result +def params_to_protocol(params: Optional[ParamsType]) -> Optional[List[NameValue]]: + if not params: + return None + if isinstance(params, dict): + return object_to_array(params) + if params.startswith("?"): + params = params[1:] + parsed = parse_qs(params) + if not parsed: + return None + out = [] + for name, values in parsed.items(): + for value in values: + out.append(NameValue(name=name, value=value)) + return out + + def file_payload_to_json(payload: FilePayload) -> ServerFilePayload: return ServerFilePayload( name=payload["name"], diff --git a/playwright/_impl/_frame.py b/playwright/_impl/_frame.py index 7dcfe0f4e..1ce813636 100644 --- a/playwright/_impl/_frame.py +++ b/playwright/_impl/_frame.py @@ -670,7 +670,6 @@ async def select_option( dict( selector=selector, timeout=timeout, - noWaitAfter=noWaitAfter, strict=strict, force=force, **convert_select_option_values(value, index, label, element), diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py index d6df048bc..91c2a460c 100644 --- a/playwright/_impl/_network.py +++ b/playwright/_impl/_network.py @@ -96,14 +96,20 @@ async def to_client_certificates_protocol( } if passphrase := clientCertificate.get("passphrase"): out_record["passphrase"] = passphrase + if pfx := clientCertificate.get("pfx"): + out_record["pfx"] = base64.b64encode(pfx).decode() if pfx_path := clientCertificate.get("pfxPath"): out_record["pfx"] = base64.b64encode( await async_readfile(pfx_path) ).decode() + if cert := clientCertificate.get("cert"): + out_record["cert"] = base64.b64encode(cert).decode() if cert_path := clientCertificate.get("certPath"): out_record["cert"] = base64.b64encode( await async_readfile(cert_path) ).decode() + if key := clientCertificate.get("key"): + out_record["key"] = base64.b64encode(key).decode() if key_path := clientCertificate.get("keyPath"): out_record["key"] = base64.b64encode( await async_readfile(key_path) diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index dbd9a36b7..98bf96cc0 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -808,13 +808,16 @@ async def fallback( ) -> None: """Route.fallback + Continues route's request with optional overrides. The method is similar to `route.continue_()` with the + difference that other matching handlers will be invoked before sending the request. + + **Usage** + 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. @@ -861,6 +864,9 @@ async def handle(route, request): await page.route(\"**/*\", handle) ``` + Use `route.continue_()` to immediately send the request to the network, other matching handlers won't be + invoked in that case. + Parameters ---------- url : Union[str, None] @@ -893,7 +899,7 @@ async def continue_( ) -> None: """Route.continue_ - Continues route's request with optional overrides. + Sends route's request to the network with optional overrides. **Usage** @@ -916,6 +922,9 @@ async def handle(route, request): in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header through redirects, use the combination of `route.fetch()` and `route.fulfill()` instead. + `route.continue_()` will immediately send the request to the network, other matching handlers won't be + invoked. Use `route.fallback()` If you want next matching handler in the chain to be invoked. + Parameters ---------- url : Union[str, None] @@ -2115,10 +2124,8 @@ async def select_option( 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. Returns ------- @@ -5316,10 +5323,8 @@ async def select_option( Maximum time 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()` 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. strict : Union[bool, None] When true, the call requires selector to resolve to a single element. If given selector resolves to more than one element, the call throws an exception. @@ -9390,7 +9395,8 @@ async def route_from_har( separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file. update_mode : Union["full", "minimal", None] When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, - cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`. + cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to + `minimal`. """ return mapping.from_maybe_impl( @@ -10749,10 +10755,8 @@ async def select_option( Maximum time 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()` 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. force : Union[bool, None] Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`. strict : Union[bool, None] @@ -13531,10 +13535,6 @@ async def new_context( Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted. proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None] Network proxy settings to use with this context. Defaults to none. - - **NOTE** For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If - all contexts override the proxy, global proxy will be never used and can be any string, for example `launch({ - proxy: { server: 'http://per-context' } })`. record_har_path : Union[pathlib.Path, str, None] Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()` @@ -13581,15 +13581,15 @@ async def new_context( Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persisted as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. @@ -13761,10 +13761,6 @@ async def new_page( Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted. proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None] Network proxy settings to use with this context. Defaults to none. - - **NOTE** For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If - all contexts override the proxy, global proxy will be never used and can be any string, for example `launch({ - proxy: { server: 'http://per-context' } })`. record_har_path : Union[pathlib.Path, str, None] Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()` @@ -13811,15 +13807,15 @@ async def new_page( Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persisted as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. @@ -14370,15 +14366,15 @@ async def launch_persistent_context( Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persisted as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. @@ -16893,10 +16889,8 @@ async def select_option( Maximum time 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()` 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. force : Union[bool, None] Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`. @@ -17479,8 +17473,8 @@ def headers(self) -> typing.Dict[str, str]: def headers_array(self) -> typing.List[NameValue]: """APIResponse.headers_array - An array with all the request HTTP headers associated with this response. 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 response HTTP headers associated with this response. Header names are not lower-cased. + Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times. Returns ------- @@ -17559,7 +17553,7 @@ async def delete( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17583,7 +17577,7 @@ async def delete( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17640,7 +17634,7 @@ async def head( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17664,7 +17658,7 @@ async def head( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17721,7 +17715,7 @@ async def get( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17757,7 +17751,7 @@ async def get( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17814,7 +17808,7 @@ async def patch( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17838,7 +17832,7 @@ async def patch( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17895,7 +17889,7 @@ async def put( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17919,7 +17913,7 @@ async def put( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17976,7 +17970,7 @@ async def post( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -18031,7 +18025,7 @@ async def post( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -18088,7 +18082,7 @@ async def fetch( url_or_request: typing.Union[str, "Request"], *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, method: typing.Optional[str] = None, headers: typing.Optional[typing.Dict[str, str]] = None, @@ -18127,7 +18121,7 @@ async def fetch( ---------- url_or_request : Union[Request, str] Target URL or Request to get all parameters from. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. method : Union[str, None] If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) or @@ -18258,15 +18252,15 @@ async def new_context( information obtained via `browser_context.storage_state()` or `a_pi_request_context.storage_state()`. Either a path to the file with saved storage, or the value returned by one of `browser_context.storage_state()` or `a_pi_request_context.storage_state()` methods. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index aa4e60166..69eb53b79 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -820,13 +820,16 @@ def fallback( ) -> None: """Route.fallback + Continues route's request with optional overrides. The method is similar to `route.continue_()` with the + difference that other matching handlers will be invoked before sending the request. + + **Usage** + 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 page.route(\"**/*\", lambda route: route.abort()) # Runs last. page.route(\"**/*\", lambda route: route.fallback()) # Runs second. @@ -873,6 +876,9 @@ def handle(route, request): page.route(\"**/*\", handle) ``` + Use `route.continue_()` to immediately send the request to the network, other matching handlers won't be + invoked in that case. + Parameters ---------- url : Union[str, None] @@ -907,7 +913,7 @@ def continue_( ) -> None: """Route.continue_ - Continues route's request with optional overrides. + Sends route's request to the network with optional overrides. **Usage** @@ -930,6 +936,9 @@ def handle(route, request): in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header through redirects, use the combination of `route.fetch()` and `route.fulfill()` instead. + `route.continue_()` will immediately send the request to the network, other matching handlers won't be + invoked. Use `route.fallback()` If you want next matching handler in the chain to be invoked. + Parameters ---------- url : Union[str, None] @@ -2129,10 +2138,8 @@ def select_option( 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. Returns ------- @@ -5415,10 +5422,8 @@ def select_option( Maximum time 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()` 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. strict : Union[bool, None] When true, the call requires selector to resolve to a single element. If given selector resolves to more than one element, the call throws an exception. @@ -9436,7 +9441,8 @@ def route_from_har( separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file. update_mode : Union["full", "minimal", None] When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, - cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`. + cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to + `minimal`. """ return mapping.from_maybe_impl( @@ -10821,10 +10827,8 @@ def select_option( Maximum time 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()` 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. force : Union[bool, None] Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`. strict : Union[bool, None] @@ -13563,10 +13567,6 @@ def new_context( Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted. proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None] Network proxy settings to use with this context. Defaults to none. - - **NOTE** For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If - all contexts override the proxy, global proxy will be never used and can be any string, for example `launch({ - proxy: { server: 'http://per-context' } })`. record_har_path : Union[pathlib.Path, str, None] Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()` @@ -13613,15 +13613,15 @@ def new_context( Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persisted as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. @@ -13795,10 +13795,6 @@ def new_page( Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted. proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None] Network proxy settings to use with this context. Defaults to none. - - **NOTE** For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If - all contexts override the proxy, global proxy will be never used and can be any string, for example `launch({ - proxy: { server: 'http://per-context' } })`. record_har_path : Union[pathlib.Path, str, None] Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()` @@ -13845,15 +13841,15 @@ def new_page( Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persisted as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. @@ -14410,15 +14406,15 @@ def launch_persistent_context( Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persisted as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. @@ -16979,10 +16975,8 @@ def select_option( Maximum time 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()` 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`. - Deprecated: This option will default to `true` in the future. + This option has no effect. + Deprecated: This option has no effect. force : Union[bool, None] Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`. @@ -17581,8 +17575,8 @@ def headers(self) -> typing.Dict[str, str]: def headers_array(self) -> typing.List[NameValue]: """APIResponse.headers_array - An array with all the request HTTP headers associated with this response. 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 response HTTP headers associated with this response. Header names are not lower-cased. + Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times. Returns ------- @@ -17663,7 +17657,7 @@ def delete( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17687,7 +17681,7 @@ def delete( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17746,7 +17740,7 @@ def head( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17770,7 +17764,7 @@ def head( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17829,7 +17823,7 @@ def get( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17865,7 +17859,7 @@ def get( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -17924,7 +17918,7 @@ def patch( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -17948,7 +17942,7 @@ def patch( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -18007,7 +18001,7 @@ def put( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -18031,7 +18025,7 @@ def put( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -18090,7 +18084,7 @@ def post( url: str, *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, headers: typing.Optional[typing.Dict[str, str]] = None, data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None, @@ -18145,7 +18139,7 @@ def post( ---------- url : str Target URL. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. headers : Union[Dict[str, str], None] Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by @@ -18204,7 +18198,7 @@ def fetch( url_or_request: typing.Union[str, "Request"], *, params: typing.Optional[ - typing.Dict[str, typing.Union[str, float, bool]] + typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str] ] = None, method: typing.Optional[str] = None, headers: typing.Optional[typing.Dict[str, str]] = None, @@ -18247,7 +18241,7 @@ def fetch( ---------- url_or_request : Union[Request, str] Target URL or Request to get all parameters from. - params : Union[Dict[str, Union[bool, float, str]], None] + params : Union[Dict[str, Union[bool, float, str]], str, None] Query parameters to be sent with the URL. method : Union[str, None] If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) or @@ -18380,15 +18374,15 @@ def new_context( information obtained via `browser_context.storage_state()` or `a_pi_request_context.storage_state()`. Either a path to the file with saved storage, or the value returned by one of `browser_context.storage_state()` or `a_pi_request_context.storage_state()` methods. - client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], keyPath: Union[pathlib.Path, str, None], pfxPath: Union[pathlib.Path, str, None], passphrase: Union[str, None]}], None] + client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None] TLS Client Authentication allows the server to request a client certificate and verify it. **Details** - An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a - single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the - certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that - the certificate is valid for. + An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, + a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, + `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided + with an exact match to the request origin that the certificate is valid for. **NOTE** Using Client Certificates in combination with Proxy Servers is not supported. diff --git a/setup.py b/setup.py index 2a6195600..425d5bd3c 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.46.0" +driver_version = "1.47.0" def extractall(zip: zipfile.ZipFile, path: str) -> None: diff --git a/tests/async/test_browsercontext_client_certificates.py b/tests/async/test_browsercontext_client_certificates.py index 6e223b9c5..9578a69f7 100644 --- a/tests/async/test_browsercontext_client_certificates.py +++ b/tests/async/test_browsercontext_client_certificates.py @@ -172,6 +172,47 @@ async def test_should_work_with_new_context(browser: Browser, assetdir: Path) -> await context.close() +async def test_should_work_with_new_context_passing_as_content( + browser: Browser, assetdir: Path +) -> None: + context = await browser.new_context( + # TODO: Remove this once we can pass a custom CA. + ignore_https_errors=True, + client_certificates=[ + { + "origin": "https://127.0.0.1:8000", + "cert": ( + assetdir / "client-certificates/client/trusted/cert.pem" + ).read_bytes(), + "key": ( + assetdir / "client-certificates/client/trusted/key.pem" + ).read_bytes(), + } + ], + ) + page = await context.new_page() + await page.goto("https://localhost:8000") + await expect(page.get_by_test_id("message")).to_have_text( + "Sorry, but you need to provide a client certificate to continue." + ) + await page.goto("https://127.0.0.1:8000") + await expect(page.get_by_test_id("message")).to_have_text( + "Hello Alice, your certificate was issued by localhost!" + ) + + response = await page.context.request.get("https://localhost:8000") + assert ( + "Sorry, but you need to provide a client certificate to continue." + in await response.text() + ) + response = await page.context.request.get("https://127.0.0.1:8000") + assert ( + "Hello Alice, your certificate was issued by localhost!" + in await response.text() + ) + await context.close() + + async def test_should_work_with_new_persistent_context( browser_type: BrowserType, assetdir: Path, launch_arguments: Dict ) -> None: diff --git a/tests/async/test_fetch_browser_context.py b/tests/async/test_fetch_browser_context.py index ffab7b77c..cc4e2b555 100644 --- a/tests/async/test_fetch_browser_context.py +++ b/tests/async/test_fetch_browser_context.py @@ -97,8 +97,48 @@ async def test_should_support_query_params( server.EMPTY_PAGE + "?p1=foo", params=expected_params ), ) - assert server_req.args["p1".encode()][0].decode() == "v1" - assert len(server_req.args["p1".encode()]) == 1 + assert list(map(lambda x: x.decode(), server_req.args["p1".encode()])) == [ + "foo", + "v1", + ] + assert server_req.args["парам2".encode()][0].decode() == "знач2" + + +@pytest.mark.parametrize( + "method", ["fetch", "delete", "get", "head", "patch", "post", "put"] +) +async def test_should_support_params_passed_as_object( + context: BrowserContext, server: Server, method: str +) -> None: + params = { + "param1": "value1", + "парам2": "знач2", + } + [server_req, _] = await asyncio.gather( + server.wait_for_request("/empty.html"), + getattr(context.request, method)(server.EMPTY_PAGE, params=params), + ) + assert server_req.args["param1".encode()][0].decode() == "value1" + assert len(server_req.args["param1".encode()]) == 1 + assert server_req.args["парам2".encode()][0].decode() == "знач2" + + +@pytest.mark.parametrize( + "method", ["fetch", "delete", "get", "head", "patch", "post", "put"] +) +async def test_should_support_params_passed_as_strings( + context: BrowserContext, server: Server, method: str +) -> None: + params = "?param1=value1¶m1=value2&парам2=знач2" + [server_req, _] = await asyncio.gather( + server.wait_for_request("/empty.html"), + getattr(context.request, method)(server.EMPTY_PAGE, params=params), + ) + assert list(map(lambda x: x.decode(), server_req.args["param1".encode()])) == [ + "value1", + "value2", + ] + assert len(server_req.args["param1".encode()]) == 2 assert server_req.args["парам2".encode()][0].decode() == "знач2" diff --git a/tests/sync/test_browsercontext_client_certificates.py b/tests/sync/test_browsercontext_client_certificates.py index 601d6eacc..7a6d4f8cf 100644 --- a/tests/sync/test_browsercontext_client_certificates.py +++ b/tests/sync/test_browsercontext_client_certificates.py @@ -168,6 +168,44 @@ def test_should_work_with_new_context(browser: Browser, assetdir: Path) -> None: context.close() +def test_should_work_with_new_context_passing_as_content( + browser: Browser, assetdir: Path +) -> None: + context = browser.new_context( + # TODO: Remove this once we can pass a custom CA. + ignore_https_errors=True, + client_certificates=[ + { + "origin": "https://127.0.0.1:8000", + "cert": ( + assetdir / "client-certificates/client/trusted/cert.pem" + ).read_bytes(), + "key": ( + assetdir / "client-certificates/client/trusted/key.pem" + ).read_bytes(), + } + ], + ) + page = context.new_page() + page.goto("https://localhost:8000") + expect(page.get_by_test_id("message")).to_have_text( + "Sorry, but you need to provide a client certificate to continue." + ) + page.goto("https://127.0.0.1:8000") + expect(page.get_by_test_id("message")).to_have_text( + "Hello Alice, your certificate was issued by localhost!" + ) + + response = page.context.request.get("https://localhost:8000") + assert ( + "Sorry, but you need to provide a client certificate to continue." + in response.text() + ) + response = page.context.request.get("https://127.0.0.1:8000") + assert "Hello Alice, your certificate was issued by localhost!" in response.text() + context.close() + + def test_should_work_with_new_persistent_context( browser_type: BrowserType, assetdir: Path, launch_arguments: Dict ) -> None: diff --git a/tests/sync/test_fetch_browser_context.py b/tests/sync/test_fetch_browser_context.py index dd10d5adf..e4d880631 100644 --- a/tests/sync/test_fetch_browser_context.py +++ b/tests/sync/test_fetch_browser_context.py @@ -89,8 +89,43 @@ def test_should_support_query_params( getattr(context.request, method)( server.EMPTY_PAGE + "?p1=foo", params=expected_params ) - assert server_req.value.args["p1".encode()][0].decode() == "v1" - assert len(server_req.value.args["p1".encode()]) == 1 + assert list(map(lambda x: x.decode(), server_req.value.args["p1".encode()])) == [ + "foo", + "v1", + ] + assert server_req.value.args["парам2".encode()][0].decode() == "знач2" + + +@pytest.mark.parametrize( + "method", ["fetch", "delete", "get", "head", "patch", "post", "put"] +) +def test_should_support_params_passed_as_object( + context: BrowserContext, server: Server, method: str +) -> None: + params = { + "param1": "value1", + "парам2": "знач2", + } + with server.expect_request("/empty.html") as server_req: + getattr(context.request, method)(server.EMPTY_PAGE, params=params) + assert server_req.value.args["param1".encode()][0].decode() == "value1" + assert len(server_req.value.args["param1".encode()]) == 1 + assert server_req.value.args["парам2".encode()][0].decode() == "знач2" + + +@pytest.mark.parametrize( + "method", ["fetch", "delete", "get", "head", "patch", "post", "put"] +) +def test_should_support_params_passed_as_strings( + context: BrowserContext, server: Server, method: str +) -> None: + params = "?param1=value1¶m1=value2&парам2=знач2" + with server.expect_request("/empty.html") as server_req: + getattr(context.request, method)(server.EMPTY_PAGE, params=params) + assert list( + map(lambda x: x.decode(), server_req.value.args["param1".encode()]) + ) == ["value1", "value2"] + assert len(server_req.value.args["param1".encode()]) == 2 assert server_req.value.args["парам2".encode()][0].decode() == "знач2" From 64c739e3a5b9d849d2645d26721a7adbadc09527 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Mon, 9 Sep 2024 16:01:45 +0200 Subject: [PATCH 2/2] update driver --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 425d5bd3c..a1cb4a6c4 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.47.0" +driver_version = "1.47.0-beta-1725889926000" def extractall(zip: zipfile.ZipFile, path: str) -> None: