Skip to content

Commit

Permalink
API: impl BrowserContext (un-)routing
Browse files Browse the repository at this point in the history
  • Loading branch information
coreyti committed Oct 10, 2024
1 parent d426394 commit cdf5d07
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 22 deletions.
95 changes: 80 additions & 15 deletions lib/playwright/browser_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ defmodule Playwright.BrowserContext do
| `context` | | The "subject" `APIRequestContext` |
| `options` | (optional) | Options (see below) |
## Options
### Options
| name | | description |
| -------- | ---------- | --------------------------------- |
Expand All @@ -1173,6 +1173,32 @@ defmodule Playwright.BrowserContext do
end
end

@doc """
Removes a route created via `Playwright.BrowserContext.route/4`.
When `handler` is not specified, removes all routes for the provided
URL/pattern.
## Usage
BrowserContext.unroute(context, "**/*")
BrowserContext.unroute(context, "**/*", handler)
## Arguments
| name | | description |
| --------- | ---------- | --------------------------------- |
| `context` | | The "subject" `APIRequestContext` |
| `url` | | A glob pattern, regex pattern, or predicate receiving a [URL]() used to register a routing handler via `route/4`. |
| `handler` | (optional) | A handler function provided when registering a routing handler via `route/4`. |
## Returns
- `BrowserContext.t()`
- `{:error, Error.t()}`
"""
@pipe {:unroute, [:context, :url]}
@pipe {:unroute, [:context, :url, :callback]}
@spec unroute(t(), binary(), function() | nil) :: t() | {:error, Error.t()}
def unroute(%BrowserContext{session: session} = context, pattern, callback \\ nil) do
with_latest(context, fn context ->
Expand All @@ -1181,12 +1207,63 @@ defmodule Playwright.BrowserContext do
handler.matcher.match != pattern || (callback && handler.callback != callback)
end)

patterns = Helpers.RouteHandler.prepare(remaining)

Channel.patch(session, {:guid, context.guid}, %{routes: remaining})
Channel.post({context, :set_network_interception_patterns}, %{patterns: patterns})
end)
end

# @spec unroute_all(t(), map()) :: t() | {:error, Error.t()}
# def unroute_all(context, options \\ %{})
@doc """
Removes all routes created via `Playwright.BrowserContext.route/4` and
`Playwright.BrowserContext.route_from_har/3`.
## Usage
BrowserContext.unroute_all(context)
BrowserContext.unroute_all(context, %{behavior: "default"})
BrowserContext.unroute_all(context, %{behavior: "ignoreErrors"})
BrowserContext.unroute_all(context, %{behavior: "wait"})
## Arguments
| name | | description |
| --------- | ---------- | --------------------------------- |
| `context` | | The "subject" `APIRequestContext` |
| `options` | (optional) | Options (see below). |
### Options
| name | | description |
| ----------- | ---------- | --------------------------------- |
| `:behavior` | (optional) | Specifies whether to wait for already running handlers, and what to do if they throw errors. |
#### Detais for `:behavior`
One of:
- `"default"` - Do not wait for current handler calls, if any, to finish.
If a handler being removed throws, it may result in an unhandled error.
- `"ignoreErrors"` - Do not wait for current handler calls, if any, to finish.
Any errors thrown by handlers being removed are silently caught.
- `"wait"` - Wait for any current handler calls to finish.
## Returns
- `BrowserContext.t()`
- `{:error, Error.t()}`
"""
@pipe {:unroute_all, [:context]}
@pipe {:unroute_all, [:context, :options]}
@spec unroute_all(t(), options()) :: t() | {:error, Error.t()}
def unroute_all(%BrowserContext{session: session} = context, options \\ %{}) do
with_latest(context, fn context ->
patterns = Helpers.RouteHandler.prepare([])

Channel.patch(session, {:guid, context.guid}, %{routes: []})
Channel.post({context, :set_network_interception_patterns}, %{patterns: patterns}, options)
end)
end

# @spec wait_for_event(t(), binary(), map()) :: map()
# def wait_for_event(context, event, options \\ %{})
Expand All @@ -1198,18 +1275,6 @@ defmodule Playwright.BrowserContext do
Playwright.BindingCall.call(binding, Map.get(context.bindings, binding.name))
end

# NOTE:
# Still need to remove the handler when it does the job. Like the following:
#
# if handler_entry.matches(request.url):
# if handler_entry.handle(route, request):
# self._routes.remove(handler_entry)
# if not len(self._routes) == 0:
# asyncio.create_task(self._disable_interception())
# break
#
# ...hoping for a test to drive that out.

# NOTE(20240525):
# Do not love this; See Page.on_route/2 (which is an exact copy of this) for why.
defp on_route(context, %{params: %{route: %{request: request} = route} = _params} = _event) do
Expand Down
111 changes: 104 additions & 7 deletions test/api/browser_context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ defmodule Playwright.BrowserContextTest do
assert_received(:intercepted)
end

test "with multiple, rolled-up handlers and `.unroute/1`", %{assets: assets, page: page} do
test "with multiple, rolled-up handlers, the latest wins", %{assets: assets, page: page} do
pid = self()
context = Page.context(page)

Expand Down Expand Up @@ -713,14 +713,12 @@ defmodule Playwright.BrowserContextTest do
BrowserContext.route(context, "**/empty.html", handler_4)

Page.goto(page, assets.empty)
BrowserContext.unroute(context, "**/empty.html", handler_4)
Page.goto(page, assets.empty)
BrowserContext.unroute(context, "**/empty.html")
Page.goto(page, assets.empty)

assert_next_receive(4)
assert_next_receive(3)
assert_next_receive(1)
assert_next_receive(4)
assert_next_receive(4)
assert_empty_mailbox()
end

Expand Down Expand Up @@ -1107,16 +1105,115 @@ defmodule Playwright.BrowserContextTest do
end
end

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

# test "on failure, returns `{:error, error}`", %{page: page}

test "with multiple, rolled-up handlers and `.unroute/1`", %{assets: assets, page: page} do
pid = self()
context = Page.context(page)

handler = fn route, marker ->
send(pid, marker)
Route.continue(route)
end

handler_4 = fn route, _request ->
handler.(route, 4)
end

BrowserContext.route(context, "**/*", fn route, _request ->
handler.(route, 1)
end)

BrowserContext.route(context, "**/*.html", fn route, _request ->
handler.(route, 2)
end)

BrowserContext.route(context, "**/empty.html", fn route, _request ->
handler.(route, 3)
end)

BrowserContext.route(context, "**/empty.html", handler_4)

Page.goto(page, assets.empty)
Page.goto(page, assets.empty)

assert_next_receive(4)
assert_next_receive(4)

BrowserContext.unroute(context, "**/empty.html", handler_4)
Page.goto(page, assets.empty)
BrowserContext.unroute(context, "**/empty.html")
Page.goto(page, assets.empty)
BrowserContext.unroute(context, "**/*.html")
Page.goto(page, assets.empty)

assert_next_receive(3)
assert_next_receive(2)
assert_next_receive(1)
assert_empty_mailbox()
end
end

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

# test "on failure, raises `RuntimeError`", %{page: page}
end

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

test "removes all routing configurations", %{assets: assets, page: page} do
pid = self()
context = Page.context(page)

handler = fn route, marker ->
send(pid, marker)
Route.continue(route)
end

BrowserContext.route(context, "**/*", fn route, _request ->
handler.(route, 1)
end)

BrowserContext.route(context, "**/*.html", fn route, _request ->
handler.(route, 2)
end)

BrowserContext.route(context, "**/empty.html", fn route, _request ->
handler.(route, 3)
end)

Page.goto(page, assets.empty)
assert_next_receive(3)

BrowserContext.unroute_all(context)
Page.goto(page, assets.empty)
Page.goto(page, assets.empty)
Page.goto(page, assets.empty)
assert_empty_mailbox()
end
end

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

# test "on failure, raises `RuntimeError`", %{page: page}
end

describe "BrowserContext.wait_for_event/2" do
Expand Down

0 comments on commit cdf5d07

Please sign in to comment.