From b248231b6610c87fcf614f60d198c8d68111d4e7 Mon Sep 17 00:00:00 2001 From: Corey Innis Date: Tue, 1 Oct 2024 22:32:46 -0400 Subject: [PATCH] Doc: add documentation for `APIRequest` --- lib/playwright/api_request.ex | 294 ++++++++++++++----------- test/api/api_request_context_test.exs | 300 +++++++++++++++++++++++++ test/api/api_request_test.exs | 301 -------------------------- 3 files changed, 465 insertions(+), 430 deletions(-) create mode 100644 test/api/api_request_context_test.exs diff --git a/lib/playwright/api_request.ex b/lib/playwright/api_request.ex index f0318cd..cfca650 100644 --- a/lib/playwright/api_request.ex +++ b/lib/playwright/api_request.ex @@ -25,29 +25,97 @@ defmodule Playwright.APIRequest do } @typedoc "Options for calls to `new_context/1`" - @type options :: map() - # %{ - # base_url: String.t(), - # extra_http_headers: map(), - # http_credentials: http_credentials(), - # ignore_https_errors: boolean(), - # proxy: proxy_settings(), - # user_agent: String.t(), - # timeout: float(), - # storage_state: storage_state() | String.t() | Path.t(), - # client_certificates: [client_certificate()] - # } + @type options :: %{ + optional(:base_url) => String.t(), + optional(:client_certificates) => [client_certificate()], + optional(:extra_http_headers) => http_headers(), + optional(:http_credentials) => http_credentials(), + optional(:ignore_https_errors) => boolean(), + optional(:proxy) => proxy_settings(), + optional(:storage_state) => storage_state() | Path.t() | String.t(), + optional(:timeout) => float(), + optional(:user_agent) => String.t() + } + + @typedoc """ + A client certificate to be used in requests. + """ + @type client_certificate :: %{ + required(:origin) => String.t(), + optional(:cert_path) => Path.t() | String.t(), + optional(:key_path) => Path.t() | String.t(), + optional(:pfx_path) => Path.t() | String.t(), + optional(:passphrase) => String.t() + } + + @typedoc """ + A `map` containing additional HTTP headers to be sent with every request. + """ + @type http_headers :: %{required(String.t()) => String.t()} + + @typedoc "HTTP authetication credentials." + @type http_credentials() :: %{ + required(:username) => String.t(), + required(:password) => String.t(), + optional(:origin) => String.t(), + optional(:send) => :always | :unauthorized + } + + @typedoc "Network proxy settings." + @type proxy_settings :: %{ + required(:server) => String.t(), + optional(:bypass) => String.t(), + optional(:username) => String.t(), + optional(:password) => String.t() + } + + @typedoc "Storage state settings." + @type storage_state :: %{ + required(:cookies) => [cookie()], + required(:origins) => [ + %{ + required(:origin) => String.t(), + required(:local_storage) => [local_storage()] + } + ] + } + + @typedoc "An HTTP cookie." + @type cookie :: %{ + required(:name) => String.t(), + required(:value) => String.t(), + required(:domain) => String.t(), + required(:path) => String.t(), + required(:expires) => float(), + required(:http_only) => boolean(), + required(:secure) => boolean(), + required(:same_site) => :lax | :none | :strict + } + + @typedoc "Local storage settings." + @type local_storage :: %{ + required(:name) => String.t(), + required(:value) => String.t() + } @doc """ Returns a new `Playwright.APIRequest`. - ## Returns + See also `Playwright.request/1` which is a more likely entry-point. - - `Playwright.APIRequest.t()` + ## Usage + + request = APIRequest.new(session) + + ## Arguments - ## Parameters + | name | description | + | --------- | -------------------------------------------- | + | `session` | The `pid` for the current Playwright session | + + ## Returns - - session ... + - `Playwright.APIRequest.t()` """ @spec new(pid()) :: t() def new(session) do @@ -60,31 +128,26 @@ defmodule Playwright.APIRequest do @doc """ Creates a new instance of `Playwright.APIRequestContext`. - ## Returns + ## Usage - - `Playwright.APIRequestContext.t()` - - `{:error, Playwright.API.Error.t()}` + request = Playwright.request(session) - ## Parameters + APIRequest.new_context(request) + APIRequest.new_context(request, options) - - request ... - - options + APIRequest.new_context!(request) + APIRequest.new_context!(request, options) - ## Options + ## Arguments + + | name | | description | + | --------- | ---------- | -------------------------- | + | `request` | | The "subject" `APIRequest` | + | `options` | (optional) | `APIRequest.options()` | - | name | type | - | ---------------------- | ------------------------ | - | `:base_url` | `String.t()` | - | `:extra_http_headers` | `map()` | - | `:http_credentials` | `[http_credential()]` | - | `:ignore_https_errors` | `boolean()` | - | `:proxy` | `proxy()` | - | `:user_agent` | `String.t()` | - | `:timeout` | `float()` | - | `:storage_state` | `storage_state()` | - | `:client_certififates` | `[client_certificate()]` | + ## Options - --- +
### Option: `:base_url` @@ -92,7 +155,7 @@ defmodule Playwright.APIRequest do consideration by using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the corresponding require URL. - Examples: + #### Examples - With `base_url: http://localhost:3000`, sending a request to `/bar.html` results in `http://localhost:3000/bar.html`. @@ -101,59 +164,82 @@ defmodule Playwright.APIRequest do - With `base_url: http://localhost:3000/foo` (without the trailing slash), navigating to `./bar.html` results in `http://localhost:3000/bar.html`. - --- +
+ + ### Option: `:client_certificates` + + A list of client certificates to be used. Each certificate instance must have + both `:cert_path` and `:key_path` or a single `:pfx_path` to load the client + certificate. Optionally, the `:passphrase` property should be provided if the + certficiate is encrypted. The `:origin` property should be provided with an + exact match to the request origin for which the certificate is valid. + + TLS client authentication allows the server to request a client certificate + and verify it. + + > #### NOTE {: .info} + > + > Using client certificates in combination with proxy servers is not supported. + + > #### NOTE {: .info} + > + > When using WebKit on macOS, accessing `localhost` will not pick up client + > certificates. As a work-around: replace `localhost` with `local.playwright`. + + #### Certificate details + + | name | | description | + | ------------- | ---------- | --------------------------------- | + | `:origin` | | Exact origin that the certificate is valid for. Origin includes https protocol, a hostname and optionally a port. | + | `:cert_path` | (optional) | Path to the file with the certificate in PEM format. | + | `:key_path` | (optional) | Path to the file with the private key in PEM format. | + | `:pfx_path` | (optional) | Path to the PFX or PKCS12 encoded private key and certificate chain. | + | `:passphrase` | (optional) | Passphrase for the private key (PEM or PFX). | + +
### Option: `:extra_http_headers` A `map` containing additional HTTP headers to be sent with every request. +
+ ### Option: `:http_credentials` Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no `:origin` is specified, the `:username` and `:password` are sent to any servers upon unauthorized responses. - | name | type | - | ---------------------- | -------------------------------------- | - | `:username` | `String.t()` | - | `:password` | `String.t()` | - | `:origin` | `String.t()` (optional) | - | `:send` | `"always", "unauthorized"` (optional) | + #### Credential details + + | name | | description | + | ----------- | ---------- | ----------- | + | `:username` | | | + | `:password` | | | + | `:origin` | (optional) | Restrain sending http credentials on specific origin (`scheme://host:port`). | + | `:send` | (optional) | This option only applies to the requests sent from corresponding `APIRequestContext` and does not affect requests sent from the browser. `:always` - `Authorization` header with basic authentication credentials will be sent with the each API request. `:unauthorized`- the credentials are only sent when 401 (Unauthorized) response with `WWW-Authenticate` header is received. Defaults to `:unauthorized`. | - --- +
### Option: `:ignore_https_errors` Whether to ignore HTTPS errors when sending network requests. Defaults to `false`. - --- +
### Option: `:proxy` Network proxy settings. - | name | type | - | ----------- | ----------------------- | - | `:server` | `String.t()` | - | `:bypass` | `String.t()` (optional) | - | `:username` | `String.t()` (optional) | - | `:password` | `String.t()` (optional) | - - --- - - ### Option: `:user_agent` - - Specific user agent to use in this context. - - --- - - ### Option: `:timeout` - - Maximum time in milliseconds to wait for the response. Defaults to `30_000` - (30 seconds). Pass `0` to disable the timeout. + | name | | description | + | ----------- | ---------- | ----------- | + | `:server` | | Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example `http://myproxy.com:3128` or `socks5://myproxy.com:3128`. Short form `myproxy.com:3128` is considered an HTTP proxy. | + | `:bypass` | (optional) | Optional comma-separated domains to bypass proxy, for example `".com, chromium.org, .domain.com"`. | + | `:username` | (optional) | Optional username to use if HTTP proxy requires authentication. | + | `:password` | (optional) | Optional password to use if HTTP proxy requires authentication. | - --- +
### Option: `:storage_state` @@ -164,51 +250,28 @@ defmodule Playwright.APIRequest do returned by one of `BrowserContext.storage_state/2` or `APIRequestContext.storage_state/2`. - One of: + | name | | description | + | ----------- | ---------- | ----------- | + | `:cookies` | | `[APIRequest.cookie()]` | + | `:origins` | | `[APIRequest.origin()]` | - - `String.t()` - - `Path.t()` - - Storage state +
- Where storage state has the following shape: - - | name | type | - | ----------- | --------- | - | `:cookies` | **TODO** | - | `:origins` | **TODO** | - - --- - - ### Option: `:client_certificates` - - TLS client authentication allows the server to request a client certificate - and verify it. - - **Details** + ### Option: `:timeout` - An array of client certificates to be used. Each certificate object must have - both `:cert_path` and `:key_path` or a single `:pfx_path` to load the client - certificate. + Maximum time in milliseconds to wait for the response. Defaults to `30_000` + (30 seconds). Pass `0` to disable the timeout. - Optionally, the `:passphrase` property should be provided if the certficiate - is encrypted. The `:origin` property should be provided with an exact match to - the request origin for which the certificate is valid. +
- **NOTES:** + ### Option: `:user_agent` - - Using client certificates in combination with proxy servers is not supported. - - When using WebKit on macOS, accessing `localhost` will not pick up client - certificates. As a work-around: replace `localhost` with `local.playwright`. + Specific user agent to use in this context. - Where each client certificate has the following shape: + ## Returns - | name | type | - | ------------- | --------------------------------- | - | `:origin` | `String.t()` | - | `:cert_path` | `Path.t(), String.t()` (optional) | - | `:key_path` | `Path.t(), String.t()` (optional) | - | `:pfx_path` | `Path.t(), String.t()` (optional) | - | `:passphrase` | `String.t()` (optional) | + - `Playwright.APIRequestContext.t()` + - `{:error, Playwright.API.Error.t()}` """ @pipe {:new_context, [:request]} @pipe {:new_context, [:request, :options]} @@ -216,31 +279,4 @@ defmodule Playwright.APIRequest do def new_context(request, options \\ %{}) do Channel.post({request, :new_request}, options) end - - # Storage State shape: - # { - # cookies: [ - # { - # name: str, - # value: str, - # domain: str, - # path: str, - # expires: float, - # httpOnly: bool, - # secure: bool, - # sameSite: Union["Lax", "None", "Strict"] - # } - # ], - # origins: [ - # { - # origin: str, - # localStorage: [ - # { - # name: str, - # value: str - # } - # ] - # } - # ] - # } end diff --git a/test/api/api_request_context_test.exs b/test/api/api_request_context_test.exs new file mode 100644 index 0000000..64e571a --- /dev/null +++ b/test/api/api_request_context_test.exs @@ -0,0 +1,300 @@ +# def test_should_work(playwright: Playwright, method: str, server: Server) -> None: +# def test_should_dispose_global_request(playwright: Playwright, server: Server) -> None: +# def test_should_support_global_user_agent_option( +# def test_should_support_global_timeout_option( +# def test_should_propagate_extra_http_headers_with_redirects( +# def test_should_support_global_http_credentials_option( +# def test_should_return_error_with_wrong_credentials( +# def test_should_work_with_correct_credentials_and_matching_origin( +# def test_should_work_with_correct_credentials_and_matching_origin_case_insensitive( +# def test_should_return_error_with_correct_credentials_and_mismatching_scheme( +# def test_should_return_error_with_correct_credentials_and_mismatching_hostname( +# def test_should_return_error_with_correct_credentials_and_mismatching_port( +# def test_should_support_global_ignore_https_errors_option( +# def test_should_resolve_url_relative_to_global_base_url_option( +# def test_should_use_playwright_as_a_user_agent( +# def test_should_return_empty_body(playwright: Playwright, server: Server) -> None: +# def test_storage_state_should_round_trip_through_file( +# def test_should_throw_an_error_when_max_redirects_is_exceeded( +# def test_should_not_follow_redirects_when_max_redirects_is_set_to_0( +# def test_should_throw_an_error_when_max_redirects_is_less_than_0( +# def test_should_serialize_null_values_in_json( + +# def test_should_work(playwright: Playwright, method: str, server: Server) -> None: +# request = playwright.request.new_context() +# response: APIResponse = getattr(request, method)(server.PREFIX + "/simple.json") +# assert response.status == 200 +# assert response.status_text == "OK" +# assert response.ok is True +# assert response.url == server.PREFIX + "/simple.json" +# assert response.headers["content-type"] == "application/json" +# assert { +# "name": "Content-Type", +# "value": "application/json", +# } in response.headers_array +# assert response.text() == ("" if method == "head" else '{"foo": "bar"}\n') + +# def test_should_dispose_global_request(playwright: Playwright, server: Server) -> None: +# request = playwright.request.new_context() +# response = request.get(server.PREFIX + "/simple.json") +# assert response.json() == {"foo": "bar"} +# response.dispose() +# with pytest.raises(Error, match="Response has been disposed"): +# response.body() + +# def test_should_support_global_user_agent_option( +# playwright: Playwright, server: Server +# ) -> None: +# request = playwright.request.new_context(user_agent="My Agent") +# response = request.get(server.PREFIX + "/empty.html") +# with server.expect_request("/empty.html") as server_req: +# request.get(server.EMPTY_PAGE) +# assert response.ok is True +# assert response.url == server.EMPTY_PAGE + +# assert server_req.value.getHeader("user-agent") == "My Agent" + +# def test_should_support_global_timeout_option( +# playwright: Playwright, server: Server +# ) -> None: +# request = playwright.request.new_context(timeout=100) +# server.set_route("/empty.html", lambda req: None) +# with pytest.raises(Error, match="Request timed out after 100ms"): +# request.get(server.EMPTY_PAGE) + +# def test_should_propagate_extra_http_headers_with_redirects( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_redirect("/a/redirect1", "/b/c/redirect2") +# server.set_redirect("/b/c/redirect2", "/simple.json") +# request = playwright.request.new_context(extra_http_headers={"My-Secret": "Value"}) +# with server.expect_request("/a/redirect1") as server_req1: +# with server.expect_request("/b/c/redirect2") as server_req2: +# with server.expect_request("/simple.json") as server_req3: +# request.get(f"{server.PREFIX}/a/redirect1") +# assert server_req1.value.getHeader("my-secret") == "Value" +# assert server_req2.value.getHeader("my-secret") == "Value" +# assert server_req3.value.getHeader("my-secret") == "Value" + +# def test_should_support_global_http_credentials_option( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_auth("/empty.html", "user", "pass") +# request1 = playwright.request.new_context() +# response1 = request1.get(server.EMPTY_PAGE) +# assert response1.status == 401 +# response1.dispose() + +# request2 = playwright.request.new_context( +# http_credentials={"username": "user", "password": "pass"} +# ) +# response2 = request2.get(server.EMPTY_PAGE) +# assert response2.status == 200 +# assert response2.ok is True +# response2.dispose() + +# def test_should_return_error_with_wrong_credentials( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_auth("/empty.html", "user", "pass") +# request = playwright.request.new_context( +# http_credentials={"username": "user", "password": "wrong"} +# ) +# response = request.get(server.EMPTY_PAGE) +# assert response.status == 401 +# assert response.ok is False + +# def test_should_work_with_correct_credentials_and_matching_origin( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_auth("/empty.html", "user", "pass") +# request = playwright.request.new_context( +# http_credentials={ +# "username": "user", +# "password": "pass", +# "origin": server.PREFIX, +# } +# ) +# response = request.get(server.EMPTY_PAGE) +# assert response.status == 200 +# response.dispose() + +# def test_should_work_with_correct_credentials_and_matching_origin_case_insensitive( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_auth("/empty.html", "user", "pass") +# request = playwright.request.new_context( +# http_credentials={ +# "username": "user", +# "password": "pass", +# "origin": server.PREFIX.upper(), +# } +# ) +# response = request.get(server.EMPTY_PAGE) +# assert response.status == 200 +# response.dispose() + +# def test_should_return_error_with_correct_credentials_and_mismatching_scheme( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_auth("/empty.html", "user", "pass") +# request = playwright.request.new_context( +# http_credentials={ +# "username": "user", +# "password": "pass", +# "origin": server.PREFIX.replace("http://", "https://"), +# } +# ) +# response = request.get(server.EMPTY_PAGE) +# assert response.status == 401 +# response.dispose() + +# def test_should_return_error_with_correct_credentials_and_mismatching_hostname( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_auth("/empty.html", "user", "pass") +# hostname = urlparse(server.PREFIX).hostname +# assert hostname +# origin = server.PREFIX.replace(hostname, "mismatching-hostname") +# request = playwright.request.new_context( +# http_credentials={"username": "user", "password": "pass", "origin": origin} +# ) +# response = request.get(server.EMPTY_PAGE) +# assert response.status == 401 +# response.dispose() + +# def test_should_return_error_with_correct_credentials_and_mismatching_port( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_auth("/empty.html", "user", "pass") +# origin = server.PREFIX.replace(str(server.PORT), str(server.PORT + 1)) +# request = playwright.request.new_context( +# http_credentials={"username": "user", "password": "pass", "origin": origin} +# ) +# response = request.get(server.EMPTY_PAGE) +# assert response.status == 401 +# response.dispose() + +# def test_should_support_global_ignore_https_errors_option( +# playwright: Playwright, https_server: Server +# ) -> None: +# request = playwright.request.new_context(ignore_https_errors=True) +# response = request.get(https_server.EMPTY_PAGE) +# assert response.status == 200 +# assert response.ok is True +# assert response.url == https_server.EMPTY_PAGE +# response.dispose() + +# def test_should_resolve_url_relative_to_global_base_url_option( +# playwright: Playwright, server: Server +# ) -> None: +# request = playwright.request.new_context(base_url=server.PREFIX) +# response = request.get("/empty.html") +# assert response.status == 200 +# assert response.ok is True +# assert response.url == server.EMPTY_PAGE +# response.dispose() + +# def test_should_use_playwright_as_a_user_agent( +# playwright: Playwright, server: Server +# ) -> None: +# request = playwright.request.new_context() +# with server.expect_request("/empty.html") as server_req: +# request.get(server.EMPTY_PAGE) +# assert str(server_req.value.getHeader("User-Agent")).startswith("Playwright/") +# request.dispose() + +# def test_should_return_empty_body(playwright: Playwright, server: Server) -> None: +# request = playwright.request.new_context() +# response = request.get(server.EMPTY_PAGE) +# body = response.body() +# assert len(body) == 0 +# assert response.text() == "" +# request.dispose() +# with pytest.raises(Error, match="Response has been disposed"): +# response.body() + +# def test_storage_state_should_round_trip_through_file( +# playwright: Playwright, tmpdir: Path +# ) -> None: +# expected: StorageState = { +# "cookies": [ +# { +# "name": "a", +# "value": "b", +# "domain": "a.b.one.com", +# "path": "/", +# "expires": -1, +# "httpOnly": False, +# "secure": False, +# "sameSite": "Lax", +# } +# ], +# "origins": [], +# } +# request = playwright.request.new_context(storage_state=expected) +# path = tmpdir / "storage-state.json" +# actual = request.storage_state(path=path) +# assert actual == expected + +# written = path.read_text("utf8") +# assert json.loads(written) == expected + +# request2 = playwright.request.new_context(storage_state=path) +# state2 = request2.storage_state() +# assert state2 == expected + +# def test_should_throw_an_error_when_max_redirects_is_exceeded( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_redirect("/a/redirect1", "/b/c/redirect2") +# server.set_redirect("/b/c/redirect2", "/b/c/redirect3") +# server.set_redirect("/b/c/redirect3", "/b/c/redirect4") +# server.set_redirect("/b/c/redirect4", "/simple.json") + +# request = playwright.request.new_context() +# for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]: +# for max_redirects in [1, 2, 3]: +# with pytest.raises(Error) as exc_info: +# request.fetch( +# server.PREFIX + "/a/redirect1", +# method=method, +# max_redirects=max_redirects, +# ) +# assert "Max redirect count exceeded" in str(exc_info) + +# def test_should_not_follow_redirects_when_max_redirects_is_set_to_0( +# playwright: Playwright, server: Server +# ) -> None: +# server.set_redirect("/a/redirect1", "/b/c/redirect2") +# server.set_redirect("/b/c/redirect2", "/simple.json") + +# request = playwright.request.new_context() +# for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]: +# response = request.fetch( +# server.PREFIX + "/a/redirect1", method=method, max_redirects=0 +# ) +# assert response.headers["location"] == "/b/c/redirect2" +# assert response.status == 302 + +# def test_should_throw_an_error_when_max_redirects_is_less_than_0( +# playwright: Playwright, +# server: Server, +# ) -> None: +# request = playwright.request.new_context() +# for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]: +# with pytest.raises(AssertionError) as exc_info: +# request.fetch( +# server.PREFIX + "/a/redirect1", method=method, max_redirects=-1 +# ) +# assert "'max_redirects' must be greater than or equal to '0'" in str(exc_info) + +# def test_should_serialize_null_values_in_json( +# playwright: Playwright, server: Server +# ) -> None: +# request = playwright.request.new_context() +# server.set_route("/echo", lambda req: (req.write(req.post_body), req.finish())) +# response = request.post(server.PREFIX + "/echo", data={"foo": None}) +# assert response.status == 200 +# assert response.text() == '{"foo": null}' +# request.dispose() diff --git a/test/api/api_request_test.exs b/test/api/api_request_test.exs index f5688a8..d916e93 100644 --- a/test/api/api_request_test.exs +++ b/test/api/api_request_test.exs @@ -49,304 +49,3 @@ defmodule Playwright.APIRequestTest do end end end - -# def test_should_work(playwright: Playwright, method: str, server: Server) -> None: -# def test_should_dispose_global_request(playwright: Playwright, server: Server) -> None: -# def test_should_support_global_user_agent_option( -# def test_should_support_global_timeout_option( -# def test_should_propagate_extra_http_headers_with_redirects( -# def test_should_support_global_http_credentials_option( -# def test_should_return_error_with_wrong_credentials( -# def test_should_work_with_correct_credentials_and_matching_origin( -# def test_should_work_with_correct_credentials_and_matching_origin_case_insensitive( -# def test_should_return_error_with_correct_credentials_and_mismatching_scheme( -# def test_should_return_error_with_correct_credentials_and_mismatching_hostname( -# def test_should_return_error_with_correct_credentials_and_mismatching_port( -# def test_should_support_global_ignore_https_errors_option( -# def test_should_resolve_url_relative_to_global_base_url_option( -# def test_should_use_playwright_as_a_user_agent( -# def test_should_return_empty_body(playwright: Playwright, server: Server) -> None: -# def test_storage_state_should_round_trip_through_file( -# def test_should_throw_an_error_when_max_redirects_is_exceeded( -# def test_should_not_follow_redirects_when_max_redirects_is_set_to_0( -# def test_should_throw_an_error_when_max_redirects_is_less_than_0( -# def test_should_serialize_null_values_in_json( - -# def test_should_work(playwright: Playwright, method: str, server: Server) -> None: -# request = playwright.request.new_context() -# response: APIResponse = getattr(request, method)(server.PREFIX + "/simple.json") -# assert response.status == 200 -# assert response.status_text == "OK" -# assert response.ok is True -# assert response.url == server.PREFIX + "/simple.json" -# assert response.headers["content-type"] == "application/json" -# assert { -# "name": "Content-Type", -# "value": "application/json", -# } in response.headers_array -# assert response.text() == ("" if method == "head" else '{"foo": "bar"}\n') - -# def test_should_dispose_global_request(playwright: Playwright, server: Server) -> None: -# request = playwright.request.new_context() -# response = request.get(server.PREFIX + "/simple.json") -# assert response.json() == {"foo": "bar"} -# response.dispose() -# with pytest.raises(Error, match="Response has been disposed"): -# response.body() - -# def test_should_support_global_user_agent_option( -# playwright: Playwright, server: Server -# ) -> None: -# request = playwright.request.new_context(user_agent="My Agent") -# response = request.get(server.PREFIX + "/empty.html") -# with server.expect_request("/empty.html") as server_req: -# request.get(server.EMPTY_PAGE) -# assert response.ok is True -# assert response.url == server.EMPTY_PAGE - -# assert server_req.value.getHeader("user-agent") == "My Agent" - -# def test_should_support_global_timeout_option( -# playwright: Playwright, server: Server -# ) -> None: -# request = playwright.request.new_context(timeout=100) -# server.set_route("/empty.html", lambda req: None) -# with pytest.raises(Error, match="Request timed out after 100ms"): -# request.get(server.EMPTY_PAGE) - -# def test_should_propagate_extra_http_headers_with_redirects( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_redirect("/a/redirect1", "/b/c/redirect2") -# server.set_redirect("/b/c/redirect2", "/simple.json") -# request = playwright.request.new_context(extra_http_headers={"My-Secret": "Value"}) -# with server.expect_request("/a/redirect1") as server_req1: -# with server.expect_request("/b/c/redirect2") as server_req2: -# with server.expect_request("/simple.json") as server_req3: -# request.get(f"{server.PREFIX}/a/redirect1") -# assert server_req1.value.getHeader("my-secret") == "Value" -# assert server_req2.value.getHeader("my-secret") == "Value" -# assert server_req3.value.getHeader("my-secret") == "Value" - -# def test_should_support_global_http_credentials_option( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_auth("/empty.html", "user", "pass") -# request1 = playwright.request.new_context() -# response1 = request1.get(server.EMPTY_PAGE) -# assert response1.status == 401 -# response1.dispose() - -# request2 = playwright.request.new_context( -# http_credentials={"username": "user", "password": "pass"} -# ) -# response2 = request2.get(server.EMPTY_PAGE) -# assert response2.status == 200 -# assert response2.ok is True -# response2.dispose() - -# def test_should_return_error_with_wrong_credentials( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_auth("/empty.html", "user", "pass") -# request = playwright.request.new_context( -# http_credentials={"username": "user", "password": "wrong"} -# ) -# response = request.get(server.EMPTY_PAGE) -# assert response.status == 401 -# assert response.ok is False - -# def test_should_work_with_correct_credentials_and_matching_origin( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_auth("/empty.html", "user", "pass") -# request = playwright.request.new_context( -# http_credentials={ -# "username": "user", -# "password": "pass", -# "origin": server.PREFIX, -# } -# ) -# response = request.get(server.EMPTY_PAGE) -# assert response.status == 200 -# response.dispose() - -# def test_should_work_with_correct_credentials_and_matching_origin_case_insensitive( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_auth("/empty.html", "user", "pass") -# request = playwright.request.new_context( -# http_credentials={ -# "username": "user", -# "password": "pass", -# "origin": server.PREFIX.upper(), -# } -# ) -# response = request.get(server.EMPTY_PAGE) -# assert response.status == 200 -# response.dispose() - -# def test_should_return_error_with_correct_credentials_and_mismatching_scheme( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_auth("/empty.html", "user", "pass") -# request = playwright.request.new_context( -# http_credentials={ -# "username": "user", -# "password": "pass", -# "origin": server.PREFIX.replace("http://", "https://"), -# } -# ) -# response = request.get(server.EMPTY_PAGE) -# assert response.status == 401 -# response.dispose() - -# def test_should_return_error_with_correct_credentials_and_mismatching_hostname( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_auth("/empty.html", "user", "pass") -# hostname = urlparse(server.PREFIX).hostname -# assert hostname -# origin = server.PREFIX.replace(hostname, "mismatching-hostname") -# request = playwright.request.new_context( -# http_credentials={"username": "user", "password": "pass", "origin": origin} -# ) -# response = request.get(server.EMPTY_PAGE) -# assert response.status == 401 -# response.dispose() - -# def test_should_return_error_with_correct_credentials_and_mismatching_port( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_auth("/empty.html", "user", "pass") -# origin = server.PREFIX.replace(str(server.PORT), str(server.PORT + 1)) -# request = playwright.request.new_context( -# http_credentials={"username": "user", "password": "pass", "origin": origin} -# ) -# response = request.get(server.EMPTY_PAGE) -# assert response.status == 401 -# response.dispose() - -# def test_should_support_global_ignore_https_errors_option( -# playwright: Playwright, https_server: Server -# ) -> None: -# request = playwright.request.new_context(ignore_https_errors=True) -# response = request.get(https_server.EMPTY_PAGE) -# assert response.status == 200 -# assert response.ok is True -# assert response.url == https_server.EMPTY_PAGE -# response.dispose() - -# def test_should_resolve_url_relative_to_global_base_url_option( -# playwright: Playwright, server: Server -# ) -> None: -# request = playwright.request.new_context(base_url=server.PREFIX) -# response = request.get("/empty.html") -# assert response.status == 200 -# assert response.ok is True -# assert response.url == server.EMPTY_PAGE -# response.dispose() - -# def test_should_use_playwright_as_a_user_agent( -# playwright: Playwright, server: Server -# ) -> None: -# request = playwright.request.new_context() -# with server.expect_request("/empty.html") as server_req: -# request.get(server.EMPTY_PAGE) -# assert str(server_req.value.getHeader("User-Agent")).startswith("Playwright/") -# request.dispose() - -# def test_should_return_empty_body(playwright: Playwright, server: Server) -> None: -# request = playwright.request.new_context() -# response = request.get(server.EMPTY_PAGE) -# body = response.body() -# assert len(body) == 0 -# assert response.text() == "" -# request.dispose() -# with pytest.raises(Error, match="Response has been disposed"): -# response.body() - -# def test_storage_state_should_round_trip_through_file( -# playwright: Playwright, tmpdir: Path -# ) -> None: -# expected: StorageState = { -# "cookies": [ -# { -# "name": "a", -# "value": "b", -# "domain": "a.b.one.com", -# "path": "/", -# "expires": -1, -# "httpOnly": False, -# "secure": False, -# "sameSite": "Lax", -# } -# ], -# "origins": [], -# } -# request = playwright.request.new_context(storage_state=expected) -# path = tmpdir / "storage-state.json" -# actual = request.storage_state(path=path) -# assert actual == expected - -# written = path.read_text("utf8") -# assert json.loads(written) == expected - -# request2 = playwright.request.new_context(storage_state=path) -# state2 = request2.storage_state() -# assert state2 == expected - -# def test_should_throw_an_error_when_max_redirects_is_exceeded( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_redirect("/a/redirect1", "/b/c/redirect2") -# server.set_redirect("/b/c/redirect2", "/b/c/redirect3") -# server.set_redirect("/b/c/redirect3", "/b/c/redirect4") -# server.set_redirect("/b/c/redirect4", "/simple.json") - -# request = playwright.request.new_context() -# for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]: -# for max_redirects in [1, 2, 3]: -# with pytest.raises(Error) as exc_info: -# request.fetch( -# server.PREFIX + "/a/redirect1", -# method=method, -# max_redirects=max_redirects, -# ) -# assert "Max redirect count exceeded" in str(exc_info) - -# def test_should_not_follow_redirects_when_max_redirects_is_set_to_0( -# playwright: Playwright, server: Server -# ) -> None: -# server.set_redirect("/a/redirect1", "/b/c/redirect2") -# server.set_redirect("/b/c/redirect2", "/simple.json") - -# request = playwright.request.new_context() -# for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]: -# response = request.fetch( -# server.PREFIX + "/a/redirect1", method=method, max_redirects=0 -# ) -# assert response.headers["location"] == "/b/c/redirect2" -# assert response.status == 302 - -# def test_should_throw_an_error_when_max_redirects_is_less_than_0( -# playwright: Playwright, -# server: Server, -# ) -> None: -# request = playwright.request.new_context() -# for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]: -# with pytest.raises(AssertionError) as exc_info: -# request.fetch( -# server.PREFIX + "/a/redirect1", method=method, max_redirects=-1 -# ) -# assert "'max_redirects' must be greater than or equal to '0'" in str(exc_info) - -# def test_should_serialize_null_values_in_json( -# playwright: Playwright, server: Server -# ) -> None: -# request = playwright.request.new_context() -# server.set_route("/echo", lambda req: (req.write(req.post_body), req.finish())) -# response = request.post(server.PREFIX + "/echo", data={"foo": None}) -# assert response.status == 200 -# assert response.text() == '{"foo": null}' -# request.dispose()