Current section

Files

Jump to
phoenix_test_playwright lib phoenix_test playwright case.ex
Raw

lib/phoenix_test/playwright/case.ex

setup_all_config_keys = ~w(browser headless slow_mo)a
setup_config_keys = ~w(screenshot trace)a
defmodule PhoenixTest.Playwright.Case do
@moduledoc """
ExUnit case module to assist with browser based tests.
`PhoenixTest.Playwright` and `PhoenixTest.Playwright.Config` explain
how to use and configure this module.
If the default setup behaviour and order does not suit you, consider
- using config opt `browser_context_opts`, which are passed to `PhoenixTest.Playwright.Browser.new_context/2`
- using config opt `browser_page_opts`, which are passed to `PhoenixTest.Playwright.BrowserContext.new_page/2`
- implementing your own `Case` (the setup functions in this module are public for your convenience)
"""
use ExUnit.CaseTemplate
alias Ecto.Adapters.SQL.Sandbox
alias PhoenixTest.Playwright
alias PhoenixTest.Playwright.Connection
using opts do
quote do
import PhoenixTest
import PhoenixTest.Playwright,
# styler:sort
only: [
add_cookies: 2,
add_session_cookie: 3,
clear_cookies: 1,
clear_cookies: 2,
click: 2,
click: 3,
click: 4,
click_button: 4,
click_link: 4,
drag: 3,
press: 3,
press: 4,
screenshot: 2,
screenshot: 3,
type: 3,
type: 4,
with_dialog: 3
]
@moduletag Keyword.delete(unquote(opts), :async)
end
end
setup_all :do_setup_all
setup :do_setup
@doc """
Merges the ExUnit context with `PhoenixTest.Playwright.Config` opts.
Uses the result to launch the browser.
Adds `:browser_id` to the context.
"""
def do_setup_all(context) do
keys = Playwright.Config.setup_all_keys()
config = context |> Map.take(keys) |> Playwright.Config.validate!() |> Keyword.take(keys)
if pool = config[:browser_pool] do
[browser_id: Playwright.BrowserPool.checkout(pool)]
else
[browser_id: launch_browser(config)]
end
end
@doc """
Merges the ExUnit context with `PhoenixTest.Playwright.Config` opts.
Uses the result to create a new browser context and page.
Adds `:conn` to the context.
"""
def do_setup(context) do
config = context |> Map.take(Playwright.Config.setup_keys()) |> Playwright.Config.validate!()
[conn: new_session(config, context)]
end
def launch_browser(opts) do
{browser, opts} = Keyword.pop!(opts, :browser)
browser_id = Connection.launch_browser(browser, opts)
on_exit(fn -> Connection.post(guid: browser_id, method: :close) end)
browser_id
end
def new_session(config, context) do
browser_context_opts =
Enum.into(config[:browser_context_opts], %{
locale: "en",
user_agent: checkout_ecto_repos(context.async) || "No user agent"
})
{:ok, browser_context_id} = Playwright.Browser.new_context(context.browser_id, browser_context_opts)
register_selector_engines!(browser_context_id)
{:ok, page_id} = Playwright.BrowserContext.new_page(browser_context_id, config[:browser_page_opts])
{:ok, _} = Playwright.Page.update_subscription(page_id, event: :console, enabled: true)
{:ok, _} = Playwright.Page.update_subscription(page_id, event: :dialog, enabled: true)
frame_id = Connection.initializer(page_id).main_frame.guid
on_exit(fn -> Connection.post(guid: browser_context_id, method: :close) end)
if config[:trace], do: trace(browser_context_id, config, context)
if config[:screenshot], do: screenshot(page_id, config, context)
Playwright.build(%{
context_id: browser_context_id,
page_id: page_id,
frame_id: frame_id,
config: config
})
end
defp register_selector_engines!(browser_context_id) do
for {name, source} <- Playwright.Selector.Engines.custom() do
{:ok, _} = Playwright.BrowserContext.register_selector_engine(browser_context_id, to_string(name), source)
end
end
defp trace(browser_context_id, config, context) do
{:ok, _} = Playwright.BrowserContext.start_tracing(browser_context_id)
File.mkdir_p!(config[:trace_dir])
file = file_name("_#{System.unique_integer([:positive, :monotonic])}.zip", context)
path = Path.join(config[:trace_dir], file)
on_exit(fn ->
_ignore_error = Playwright.BrowserContext.stop_tracing(browser_context_id, path)
if config[:trace] == :open do
System.cmd(
Playwright.Config.global(:runner),
["playwright", "show-trace", Path.join(File.cwd!(), path)],
cd: Playwright.Config.global(:assets_dir)
)
end
end)
end
defp screenshot(page_id, config, context) do
file = file_name(".png", context)
on_exit(fn ->
Playwright.screenshot(%{page_id: page_id}, file, config[:screenshot])
end)
end
defp file_name(suffix, %{module: module, test: test}) do
"Elixir." <> module = to_string(module)
time = :second |> :erlang.system_time() |> to_string()
String.replace("#{module}.#{test}_#{time}#{suffix}", ~r/[^a-zA-Z0-9\.]/, "_")
end
@includes_ecto Code.ensure_loaded?(Sandbox) &&
Code.ensure_loaded?(Phoenix.Ecto.SQL.Sandbox)
if @includes_ecto do
defp checkout_ecto_repos(async?) do
otp_app = Application.fetch_env!(:phoenix_test, :otp_app)
repos = Application.get_env(otp_app, :ecto_repos, [])
repos
|> Enum.map(&checkout_ecto_repo(&1, async?))
|> Phoenix.Ecto.SQL.Sandbox.metadata_for(self())
|> Phoenix.Ecto.SQL.Sandbox.encode_metadata()
end
defp checkout_ecto_repo(repo, async?) do
case Sandbox.checkout(repo) do
:ok -> :ok
{:already, :allowed} -> :ok
{:already, :owner} -> :ok
end
if not async?, do: Sandbox.mode(repo, {:shared, self()})
repo
end
else
defp checkout_ecto_repos(_) do
nil
end
end
end