Skip to content

Commit

Permalink
API: impl APIRequestContext.storage_state/2
Browse files Browse the repository at this point in the history
A "WIP" change... still need to implement the (optional) saving and reading of state to/from disk.
  • Loading branch information
coreyti committed Oct 3, 2024
1 parent b534146 commit 597c3f0
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 16 deletions.
3 changes: 2 additions & 1 deletion lib/playwright/api_request.ex
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ defmodule Playwright.APIRequest do
required(:expires) => float(),
required(:http_only) => boolean(),
required(:secure) => boolean(),
required(:same_site) => :lax | :none | :strict
# same_site: "Lax" | "None" | "Strict"
required(:same_site) => String.t()
}

@typedoc "Local storage settings."
Expand Down
92 changes: 77 additions & 15 deletions lib/playwright/api_request_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ defmodule Playwright.APIRequestContext do
"""

use Playwright.SDK.ChannelOwner
alias ElixirLS.LanguageServer.Providers.Completion.Reducers.Returns
alias Playwright.APIRequestContext
alias Playwright.APIResponse
alias Playwright.Request
Expand Down Expand Up @@ -90,6 +91,35 @@ defmodule Playwright.APIRequestContext do
@typedoc "Data serializable as JSON."
@type serializable :: list() | map()

@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()
}

# API
# ----------------------------------------------------------------------------

Expand Down Expand Up @@ -145,18 +175,24 @@ defmodule Playwright.APIRequestContext do
| name | | description |
| ---------------- | ---------- | --------------------------------- |
| `context` | | The "subject" `APIRequestContext` |
| `reason` | (optional) | The reason to be reported to any operations interrupted by the context disposal. |
| `options` | (optional) | Options (see below) |
## Options
| name | | description |
| -------- | ---------- | --------------------------------- |
| `reason` | (optional) | The reason to be reported to any operations interrupted by the context disposal. |
## Returns
- `:ok`
- `{:error, %Error{}}`
"""
@spec dispose(t(), String.t()) :: :ok
def dispose(context, reason \\ nil)
@spec dispose(t(), map()) :: :ok | {:error, Error.t()}
def dispose(context, options \\ %{})

def dispose(%APIRequestContext{} = context, reason) do
case Channel.post({context, "dispose"}, %{reason: reason}, %{refresh: false}) do
def dispose(%APIRequestContext{} = context, options) do
case Channel.post({context, "dispose"}, options, %{refresh: false}) do
{:error, %Playwright.API.Error{} = error} ->
{:error, error}

Expand Down Expand Up @@ -400,14 +436,6 @@ defmodule Playwright.APIRequestContext do
Function invocation will populate request cookies from the context, and update
context cookies from the response. Calls automatically follow redirects.
## Arguments
| name | | description |
| ---------------- | ---------- | --------------------------------- |
| `context` | | The "subject" `APIRequestContext` |
| `url` | | Target URL |
| `options` | (optional) | `APIRequestContext.options()` |
## Usage
request = Playwright.request(session)
Expand All @@ -417,6 +445,14 @@ defmodule Playwright.APIRequestContext do
data: %{title: "Updated"}
})
## Arguments
| name | | description |
| ---------------- | ---------- | --------------------------------- |
| `context` | | The "subject" `APIRequestContext` |
| `url` | | Target URL |
| `options` | (optional) | `APIRequestContext.options()` |
## Options
See "Shared options" above.
Expand All @@ -433,6 +469,32 @@ defmodule Playwright.APIRequestContext do
fetch(context, url, Map.merge(options, %{method: "PUT"}))
end

# @spec storage_state(t(), options()) :: StorageState.t()
# def storage_state(context, options \\ %{})
@doc """
Returns storage state for this request context.
The storage state contains current cookies and a local storage snapshot if it
was passed to the initializer.
## Arguments
| name | | description |
| ---------------- | ---------- | --------------------------------- |
| `context` | | The "subject" `APIRequestContext` |
| `options` | (optional) | Options (see below) |
## Options
| name | | description |
| -------- | ---------- | --------------------------------- |
| `path` | (optional) | **NOT IMPLEMENTED** The file path to save the storage state to. If path is a relative path, then it is resolved relative to current working directory. If no path is provided, storage state is still returned, but won't be saved to the disk. |
## Returns
- `storage_state()`
- `{:error, Error.t()}`
"""
@spec storage_state(t(), map()) :: storage_state()
def storage_state(context, options \\ %{}) do
Channel.post({context, :storage_state}, options)
end
end
5 changes: 5 additions & 0 deletions lib/playwright/sdk/channel/response.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ defmodule Playwright.SDK.Channel.Response do
nil
end

# ?
defp parse(list, _catalog) when is_list(list) do
list
end

defp resolve(session, catalog, owner, event) do
bindings = Map.get(Channel.Session.bindings(session), {owner.guid, event.type}, [])

Expand Down
44 changes: 44 additions & 0 deletions test/api/api_request_context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ defmodule Playwright.APIRequestContextTest do
assert :ok = APIRequestContext.dispose(context)
assert {:error, %{type: "TargetClosedError"}} = APIResponse.body(response)
end

test "succeeds when provided a 'reason'", %{session: session} do
context = Playwright.request(session) |> APIRequest.new_context()
assert :ok = APIRequestContext.dispose(context, %{reason: "Done!"})
end
end

describe "APIRequestContext.fetch/3" do
Expand Down Expand Up @@ -88,5 +93,44 @@ defmodule Playwright.APIRequestContextTest do
end

describe "APIRequestContext.storage_state/2" do
test "(WIP) on success, ...", %{session: session} do
# python: test_storage_state_should_round_trip_through_file
# ---
context =
Playwright.request(session)
|> APIRequest.new_context(%{
storage_state: %{
cookies: [
%{
name: "cookie name",
value: "cookie value",
domain: "example.com",
path: "/",
expires: -1,
http_only: false,
secure: false,
same_site: "Lax"
}
],
origins: []
}
})

assert [
cookies: [
%{
name: "cookie name",
value: "cookie value",
domain: "example.com",
path: "/",
expires: -1,
httpOnly: false,
secure: false,
sameSite: "Lax"
}
],
origins: []
] = APIRequestContext.storage_state(context)
end
end
end

0 comments on commit 597c3f0

Please sign in to comment.