Packages

Playwright is an Elixir library to automate Chromium, Firefox and WebKit browsers with a single API. Playwright delivers automation that is ever-green, capable, reliable and fast.

Current section

Files

Jump to
playwrightais lib playwright channel_owner.ex
Raw

lib/playwright/channel_owner.ex

defmodule Playwright.ChannelOwner do
@moduledoc false
@callback init(struct(), map()) :: {atom(), struct()}
@optional_callbacks init: 2
defmacro __using__(_) do
quote do
use Playwright.ChannelOwner.Macros
@behaviour Playwright.ChannelOwner
@derive {Jason.Encoder, only: [:guid]}
@derive {Inspect, only: [:guid] ++ @properties}
import Playwright.Extra.Map
alias Playwright.Channel
alias Playwright.Channel.Event
defstruct @properties ++ [:session, :guid, :initializer, :listeners, :parent, :type]
@typedoc """
%#{String.replace_prefix(inspect(__MODULE__), "Elixir.", "")}{}
"""
@type t() :: %__MODULE__{}
@doc false
@spec new(struct(), map()) :: {term(), struct()}
def new(parent, %{guid: guid, type: type, initializer: initializer} = args) do
base = %__MODULE__{
session: parent.session,
guid: guid,
initializer: initializer,
listeners: %{},
parent: parent,
type: type
}
initializer = deep_snakecase_keys(initializer)
init(struct(base, initializer), initializer)
end
@doc """
Optional **callback** implementation for `Playwright.ChannelOwner.init/2`.
If implemented, the callback will receive:
1. The newly created "channel owner" struct.
2. The `:initializer` received from the Playwright browser server.
The implementation has the option of "patching" the struct as stored in
the catalog, and/or binding event handlers.
## Example
def init(%{session: session} = owner, _initializer) do
Channel.bind(session, {:guid, owner.guid}, :close, fn event ->
Logger.warn("Closing \#{inspect(event.target)}")
end)
{:ok, %{owner | version: "1.2.3"}}
end
## Returns
- `{:ok, struct()}`
## Arguments
| key/name | type | | description |
| ------------- | ------ | ---------- | ----------- |
| `owner` | param | `struct()` | The newly created channel owner (resource). |
| `initializer` | param | `struct()` | The initializer received from with the channel owner instance was derived. |
"""
@spec init(struct(), map()) :: {atom(), struct()}
def init(owner, _initializer) do
{:ok, owner}
end
defoverridable(init: 2)
require Logger
@doc false
@spec on_event(struct(), Event.t()) :: {term(), struct()}
def on_event(owner, %Event{} = event) do
listeners = Map.get(owner.listeners, event.type, [])
event =
Enum.reduce(listeners, event, fn callback, acc ->
case callback.(acc) do
{:patch, target} ->
Map.put(acc, :target, target)
_ok ->
acc
end
end)
{:ok, event.target}
end
# def find(session, {:guid, guid}) when is_binary(guid) do
defp with_latest(%{session: session} = owner, task) do
Channel.find(session, {:guid, owner.guid}) |> task.()
end
end
end
@doc false
def from(params, parent) do
module(params).new(parent, params)
end
# private
# ------------------------------------------------------------------------
defp module(%{type: "Playwright"}) do
Playwright
end
defp module(%{type: type}) do
String.to_existing_atom("Elixir.Playwright.#{type}")
rescue
ArgumentError ->
message = "ChannelOwner of type #{inspect(type)} is not yet defined"
exit(message)
end
# ChannelOwner macros
# ---------------------------------------------------------------------------
defmodule Macros do
@moduledoc false
alias Playwright.Channel
defmacro __using__(_args) do
Module.register_attribute(__CALLER__.module, :properties, accumulate: true)
quote do
import Kernel, except: [@: 1]
import unquote(__MODULE__), only: [@: 1]
end
end
def do_at(module, arg, options \\ []) do
Module.put_attribute(module, :properties, arg)
doc = Keyword.get(options, :doc, false)
quote do
@doc unquote(doc)
@spec unquote(arg)(t()) :: term()
def unquote(arg)(owner) do
owner =
if Map.has_key?(owner, :is_closed) && owner.is_closed do
owner
else
Channel.find(owner.session, {:guid, owner.guid})
end
field = Map.get(owner, unquote(arg))
if is_map(field) && Map.has_key?(field, :guid) do
Channel.find(owner.session, {:guid, field.guid})
else
field
end
end
end
end
defmacro @{:property, _meta, [arg]} do
do_at(__CALLER__.module, arg)
end
defmacro @{:property, _meta, [arg, arg2]} do
{:%{}, _, actual} = arg2
do_at(__CALLER__.module, arg, actual)
end
defmacro @expr do
quote do
Kernel.@(unquote(expr))
end
end
end
end