Skip to content

Commit

Permalink
API: impl some BrowserContext and fix/clean docs & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
coreyti committed Oct 10, 2024
1 parent bbbef41 commit d426394
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 38 deletions.
4 changes: 2 additions & 2 deletions lib/playwright/api_request_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ defmodule Playwright.APIRequestContext do
| 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. |
| `path` | (optional) | The file path to save the storage state. 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
Expand All @@ -521,7 +521,7 @@ defmodule Playwright.APIRequestContext do
"""
@pipe {:storage_state, [:context]}
@pipe {:storage_state, [:context, :options]}
@spec storage_state(t(), opts_storage()) :: storage_state()
@spec storage_state(t(), opts_storage()) :: storage_state() | {:error, Error.t()}
def storage_state(context, options \\ %{}) do
{path, options} = Map.pop(options, :path)

Expand Down
82 changes: 78 additions & 4 deletions lib/playwright/browser_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ defmodule Playwright.BrowserContext do
optional(:accuracy) => number()
}

@typedoc "Local storage settings."
@type local_storage :: %{
required(:name) => String.t(),
required(:value) => String.t()
}

@typedoc "A map/struct providing generic call options"
@type options :: map()

Expand All @@ -228,6 +234,11 @@ defmodule Playwright.BrowserContext do
optional(:times) => number()
}

@typedoc "Options for `storage_state/2`."
@type opts_storage :: %{
optional(:path) => String.t()
}

@typedoc "A permission available for `grant_permissions/3`."
@type permission :: String.t() | atom()

Expand All @@ -243,6 +254,17 @@ defmodule Playwright.BrowserContext do
| function()
| String.t()

@typedoc "Storage state settings."
@type storage_state :: %{
required(:cookies) => [cookie()],
required(:origins) => [
%{
required(:origin) => String.t(),
required(:local_storage) => [local_storage()]
}
]
}

@typedoc "A string URL"
@type url :: String.t()

Expand Down Expand Up @@ -1087,17 +1109,69 @@ defmodule Playwright.BrowserContext do
Channel.post({context, :set_geolocation})
end

@doc """
Configures whether the browser context should emulate being offline.
## Usage
BrowserContext.set_offline(context, true)
BrowserContext.set_offline(context, false)
## Arguments
| name | | description |
| --------- | ---------- | ------------------------------- |
| `context` | | The "subject" `BrowserContext`. |
| `offline` | | Whether to emulate the network being offline. |
## Returns
- `BrowserContext.t()`
- `{:error, Error. t()}`
"""
@pipe {:set_offline, [:context, :offline]}
@spec set_offline(t(), boolean()) :: t() | {:error, Error.t()}
def set_offline(%BrowserContext{} = context, offline) do
Channel.post({context, :set_offline}, %{offline: offline})
end

# ---
@doc """
Returns storage state for this browser context.
# @spec storage_state(t(), String.t()) :: storage_state()
# def storage_state(context, path \\ nil)
The storage state contains current cookies and a local storage snapshot.
# ---
## Arguments
| name | | description |
| ---------------- | ---------- | --------------------------------- |
| `context` | | The "subject" `APIRequestContext` |
| `options` | (optional) | Options (see below) |
## Options
| name | | description |
| -------- | ---------- | --------------------------------- |
| `path` | (optional) | The file path to save the storage state. 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(), opts_storage()) :: storage_state() | {:error, Error.t()}
def storage_state(%BrowserContext{} = context, options \\ %{}) do
{path, options} = Map.pop(options, :path)

case Channel.post({context, :storage_state}, options) do
{:error, _} = error ->
error

result ->
result = Map.new(result)
path && File.write!(path, Jason.encode!(result))
result
end
end

@spec unroute(t(), binary(), function() | nil) :: t() | {:error, Error.t()}
def unroute(%BrowserContext{session: session} = context, pattern, callback \\ nil) do
Expand Down
2 changes: 0 additions & 2 deletions test/api/api_request_context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,6 @@ defmodule Playwright.APIRequestContextTest do

describe "APIRequestContext.storage_state/2" do
test "(WIP) on success, ...", %{session: session} do
# python: test_storage_state_should_round_trip_through_file
# ---
slug = DateTime.utc_now() |> DateTime.to_unix()
path = "storage-state-#{slug}.json"

Expand Down
88 changes: 84 additions & 4 deletions test/api/browser_context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -937,13 +937,14 @@ defmodule Playwright.BrowserContextTest do
end

# skip: See documentation comment for `BrowserContext.set_geolocation/2`
@tag :skip
describe "BrowserContext.set_geolocation/2" do
@tag :skip
test "on success, returns the 'subject' `BrowserContext`", %{page: page} do
context = Page.context(page)
assert %BrowserContext{} = BrowserContext.set_geolocation(context, nil)
end

@tag :skip
test "on failure, returns `{:error, error}`", %{page: page} do
context = Page.context(page)
context = %{context | guid: "bogus"}
Expand All @@ -952,7 +953,8 @@ defmodule Playwright.BrowserContextTest do
BrowserContext.set_geolocation(context, %{})
end

test "...", %{assets: assets, page: page} do
@tag :skip
test "mimics geolocation settings in the browser context", %{assets: assets, page: page} do
context = Page.context(page)
BrowserContext.grant_permissions(context, ["geolocation"])

Expand All @@ -966,18 +968,20 @@ defmodule Playwright.BrowserContextTest do
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
}))
""")
|> IO.inspect(label: "geolocation")

assert %{latitude: 10, longitude: 10} = geolocation
end
end

@tag :skip
describe "BrowserContext.set_geolocation!/2" do
@tag :skip
test "on success, returns the 'subject' `BrowserContext`", %{page: page} do
context = Page.context(page)
assert %BrowserContext{} = BrowserContext.set_geolocation!(context, %{latitude: 0, longitude: 0})
end

@tag :skip
test "on failure, raises `RuntimeError`", %{page: page} do
assert_raise RuntimeError, "Target page, context or browser has been closed", fn ->
context = Page.context(page)
Expand All @@ -988,12 +992,20 @@ defmodule Playwright.BrowserContextTest do
end

describe "BrowserContext.set_offline/2" do
test "returns 'subject'", %{page: page} do
test "on success, returns the 'subject' `BrowserContext`", %{page: page} do
context = Page.context(page)
assert %BrowserContext{} = BrowserContext.set_offline(context, false)
assert %BrowserContext{} = BrowserContext.set_offline(context, true)
end

test "on failure, returns `{:error, error}`", %{page: page} do
context = Page.context(page)
context = %{context | guid: "bogus"}

assert {:error, %Error{message: "Target page, context or browser has been closed"}} =
BrowserContext.set_offline(context, true)
end

@tag without: [:page]
test "using initial option", %{assets: assets, browser: browser} do
context = Browser.new_context(browser, %{offline: true})
Expand Down Expand Up @@ -1022,9 +1034,77 @@ defmodule Playwright.BrowserContextTest do
end

describe "BrowserContext.set_offline!/2" do
test "on success, returns the 'subject' `BrowserContext`", %{page: page} do
context = Page.context(page)
assert %BrowserContext{} = BrowserContext.set_offline!(context, false)
assert %BrowserContext{} = BrowserContext.set_offline!(context, true)
end

test "on failure, raises `RuntimeError`", %{page: page} do
assert_raise RuntimeError, "Target page, context or browser has been closed", fn ->
context = Page.context(page)
context = %{context | guid: "bogus"}
BrowserContext.set_offline!(context, true)
end
end
end

describe "BrowserContext.storage_state/2" do
test "on success, returns storage state JSON", %{browser: browser} do
storage = %{
cookies: [
%{
name: "cookie name",
value: "cookie value",
domain: "example.com",
path: "/",
expires: -1,
httpOnly: false,
secure: false,
sameSite: "Lax"
}
],
origins: []
}

context = Browser.new_context(browser, %{storage_state: storage})
assert ^storage = BrowserContext.storage_state(context)
end

test "on failure, returns `{:error, error}`", %{browser: browser} do
context = Browser.new_context(browser, %{storage_state: %{}})
context = %{context | guid: "bogus"}
assert {:error, %Error{}} = BrowserContext.storage_state(context)
end

test "given the `:path` option, writes the state to disk", %{browser: browser} do
slug = DateTime.utc_now() |> DateTime.to_unix()
path = "storage-state-#{slug}.json"

storage = %{
cookies: [
%{
name: "cookie name",
value: "cookie value",
domain: "example.com",
path: "/",
expires: -1,
httpOnly: false,
secure: false,
sameSite: "Lax"
}
],
origins: []
}

context = Browser.new_context(browser, %{storage_state: storage})

assert ^storage = BrowserContext.storage_state(context, %{path: path})
assert(File.exists?(path))
assert(Jason.decode!(File.read!(path)))

File.rm!(path)
end
end

describe "BrowserContext.unroute/2" do
Expand Down
Loading

0 comments on commit d426394

Please sign in to comment.