### Install Playwright and Browsers (npm) Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest These npm commands are used to install Playwright as a development dependency and then install the necessary browser binaries for Playwright to use. This is a prerequisite for running Playwright tests. ```bash npm --prefix assets i -D playwright npm --prefix assets exec -- playwright install chromium --with-deps ``` -------------------------------- ### Playwright Test with Browser Session (PhoenixTest.Playwright.Case) Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This example demonstrates using `PhoenixTest.Playwright.Case` to interact with a browser session. It highlights that `conn` in this context is a Playwright session, not a `Plug.Conn`, allowing for browser-specific actions like visiting a URL and evaluating JavaScript in the browser console. ```elixir defmodule MyTest do use PhoenixTest.Playwright.Case, async: true # `conn` isn't a `Plug.Conn` but a Playwright session. # We use the name `conn` anyway so you can easily switch `PhoenixTest` drivers. test "in browser", %{conn: conn} do conn |> visit(~p"/") |> unwrap(&Frame.evaluate(&1.frame_id, "console.log('Hey')")) end ``` -------------------------------- ### Run Feature Tests with PhoenixTestPlaywright Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This example demonstrates how to define and run feature tests using PhoenixTest.Playwright.Case. It showcases asynchronous test execution, parameterization for different browsers, controlling browser visibility and speed, and basic interaction with a web page including navigation, form filling, and assertions. Dependencies include `PhoenixTest.Playwright.Case`. ```elixir defmodule Features.RegisterTest do use PhoenixTest.Playwright.Case, async: true, parameterize: [ %{browser: :chromium}, %{browser: :firefox} ], headless: false, slow_mo: :timer.seconds(1) @tag trace: :open test "register", %{conn: conn} do conn |> visit(~p"/") |> click_link("Register") |> fill_in("Email", with: "f@ftes.de") |> click_button("Create an account") |> assert_has(".text-rose-600", text: "required") |> screenshot("error.png", full_page: true) end end ``` -------------------------------- ### Override Playwright Test Options (headless, slow_mo) Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This example shows how to override global Playwright configuration options directly within a test module. Here, `headless` is set to `false` to display the browser window, and `slow_mo` introduces a delay between interactions for debugging purposes. ```elixir defmodule DebuggingFeatureTest do use PhoenixTest.Playwright.Case, async: true, # Show browser and pause 1 second between every interaction headless: false, slow_mo: :timer.seconds(1) end ``` -------------------------------- ### Add PhoenixTestPlaywright Dependency (mix.exs) Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This snippet shows how to add the `phoenix_test_playwright` dependency to your `mix.exs` file. It's configured to be used only during testing and doesn't require runtime functionality. ```elixir defmodule MyProject.Mixfile do use Mix def project do [ # ... other project configurations deps: deps() ] end def deps do [ {:phoenix_test_playwright, "~> 0.4", only: :test, runtime: false} # ... other dependencies ] end end ``` -------------------------------- ### Handle Browser Dialogs with PhoenixTest Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This snippet shows how to handle browser dialogs (alert, confirm, prompt) using `with_dialog`. It takes a callback function that determines how to respond to each dialog. Tests using this function should include the `@tag accept_dialogs: false` to prevent default acceptance. ```elixir @tag accept_dialogs: false test "conditionally handle dialog", %{conn: conn} do conn |> visit("/") |> with_dialog( fn %{message: "Are you sure?"} -> :accept %{message: "Enter the magic number"} -> {:accept, "42"} %{message: "Self destruct?"} -> :dismiss end, fn conn -> conn |> click_button("Delete") |> assert_has(".flash", text: "Deleted") end ) end ``` -------------------------------- ### Configure PhoenixTest for Playwright (config/test.exs) Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This configuration block in `config/test.exs` sets up the `phoenix_test` application, specifying the OTP app and providing Playwright-specific options. These options control browser behavior like the default browser, headless mode, JavaScript logging, screenshotting, tracing, and timeouts. ```elixir config :phoenix_test, otp_app: :your_app, playwright: [ browser: :chromium, headless: System.get_env("PW_HEADLESS", "true") in ~w(t true), js_logger: false, screenshot: System.get_env("PW_SCREENSHOT", "false") in ~w(t true), trace: System.get_env("PW_TRACE", "false") in ~w(t true), browser_launch_timeout: 10_000, ] ``` -------------------------------- ### Manually Capture Screenshots Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This Elixir code demonstrates how to capture a screenshot of the entire page using the `screenshot/2` function. The screenshot is saved to a file named 'home.png'. ```elixir |> visit(~p"/") |> screenshot("home.png") # captures entire page by default, not just viewport ``` -------------------------------- ### Take Screenshots with PhoenixTest Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This snippet demonstrates how to take screenshots using the `screenshot` function in PhoenixTest. It allows specifying the filename and path for the saved image. No specific dependencies are mentioned beyond the PhoenixTest library itself. ```elixir |> screenshot("my-screenshot.png") |> screenshot("my-test/my-screenshot.jpg") ``` -------------------------------- ### Wait for LiveView Connection Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This Elixir code snippet shows how to ensure a LiveView connection is established before proceeding with further test actions. It uses `visit` to navigate to the page and `assert_has` to wait for the presence of an element indicating a connected LiveView (`body .phx-connected`). ```elixir |> visit(~p"/") |> assert_has("body .phx-connected") # now continue, playwright has waited for LiveView to connect ``` -------------------------------- ### Custom Playwright Feature: Assert File Download Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This Elixir function asserts that a file download has occurred with a specific filename and content. It listens for the Playwright 'download' event, waits for the file to be written to disk, and then verifies its name and content. It uses a helper function `wait_for_file` to handle asynchronous file creation. ```elixir # In your test, first call `|> tap(&Connection.subscribe(&1.page_id))` def assert_download(conn, name, contains: content) do assert_receive({:playwright, %{method: :download} = download_msg}, 2000) path = Connection.initializer(download_msg.params.artifact.guid).absolute_path wait_for_file(path) assert download_msg.params.suggested_filename =~ name assert File.read!(path) =~ content conn end ``` -------------------------------- ### Using Ecto SQL Sandbox with Playwright Tests Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This snippet shows how PhoenixTest.Playwright.Case automatically handles Ecto SQL Sandbox for concurrent browser tests. It ensures that each test runs in isolation by providing a user agent referencing your Ecto repositories, preventing data interference between tests. ```elixir defmodule MyTest do use PhoenixTest.Playwright.Case, async: true end ``` -------------------------------- ### Generic Playwright Assertion Helper Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This is a generic helper function for Playwright assertions. It takes connection parameters and options, unwraps the Playwright frame, performs an expectation, and asserts the result. It supports negating the assertion using the `:is_not` option. ```elixir def assert_found(conn, params, opts \ []) do is_not = Keyword.get(opts, :is_not, false) params = Enum.into(params, %{is_not: is_not}) unwrap(conn, fn frame_id -> {:ok, found} = Frame.expect(frame_id, params) if is_not, do: refute(found), else: assert(found) end) end ``` -------------------------------- ### Verify Email HTML with Plug.Swoosh.MailboxPreview Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This Elixir code demonstrates how to interact with and verify the content of emails sent via `Plug.Swoosh` within a Playwright test. It involves navigating to the mailbox preview, clicking on a specific email, and then interacting with elements within an iframe to confirm actions and assert expected flash messages. ```elixir |> visit(~p"/dev/mailbox") |> click_link("Confirmation instructions") |> within("iframe >> internal:control=enter-frame", fn conn -> conn |> click_link("Confirm account") |> click_button("Confirm my account") |> assert_has("#flash-info", text: "User confirmed") end) ``` -------------------------------- ### Simulate Typing with PhoenixTest Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This function simulates user typing into a specified element. It supports an optional `:delay` option to control the time between key presses. The function takes a selector and the text to type as input. It's recommended to use `PhoenixTest.fill_in/4` in most cases. ```elixir @spec type(t(), selector(), String.t(), [{:delay, non_neg_integer()}]) :: t() |> type("#id", "some text") |> type(Selector.role("heading", "Untitled", exact: true), "New title") ``` -------------------------------- ### Add Session Cookie for Emulation - Elixir Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest Adds a Plug.Session cookie to the browser context, useful for emulating logged-in users. The cookie's :value must be a map, and session_options are identical to those used when writing plug Plug.Session. ```elixir add_session_cookie(conn, cookie, session_options) ``` -------------------------------- ### Helper Function to Wait for File Existence Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This recursive Elixir function waits for a file to exist at the given path. It checks for the file's existence periodically with a specified `wait_for_ms` interval, up to a maximum `remaining_ms`. If the file does not exist within the time limit, it raises a 'flunk' error. ```elixir defp wait_for_file(path, remaining_ms \ 2000, wait_for_ms \ 100) defp wait_for_file(path, remaining_ms, _) when remaining_ms <= 0, do: flunk("File #{path} does not exist") defp wait_for_file(path, remaining_ms, wait_for_ms) do if File.exists?(path) do :ok else Process.sleep(wait_for_ms) wait_for_file(path, remaining_ms - wait_for_ms, wait_for_ms) end end ``` -------------------------------- ### Handle Browser Dialogs - Elixir Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest Manages browser dialogs (alert, confirm, prompt) by executing a callback function while the dialog is active. This is useful for automating interactions with these native browser prompts. ```elixir with_dialog(session, callback, fun) ``` -------------------------------- ### Handle Test Failures in CI (Timeout) Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This section provides strategies for addressing test timeouts in CI environments. It suggests limiting test concurrency, increasing the Playwright timeout configuration, and utilizing more powerful CI runners. ```bash # Limit concurrency for GitHub CI shared runners mix test --max-cases 1 ``` ```elixir # Increase timeout configuration config :phoenix_test, playwright: [timeout: :timer.seconds(4)] ``` -------------------------------- ### Simulate Key Presses in Browser - Elixir Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest Focuses an element and simulates pressing keyboard keys. Supports special keys, modifiers, and combinations. Use type/4 for simple text input without special keys. ```elixir press(conn, selector, key, opts \\ []) ## Options * `:delay` (`non_neg_integer/0`) - Time to wait between keydown and keyup in milliseconds. The default value is `0`. ## Examples ```elixir |> press("#id", "Control+Shift+T") |> press(Selector.button("Submit", exact: true), "Enter") ``` ``` -------------------------------- ### Take Page Screenshot - Elixir Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest Captures a screenshot of the current page and saves it to a specified file path. The file type is determined by the extension, and saving location is configured via :screenshot_dir in PhoenixTest.Playwright.Config. ```elixir screenshot(conn, file_path, opts \\ []) ``` -------------------------------- ### Simulate User Typing - Elixir Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest Focuses on a specified element and simulates user typing. This function is intended for basic text input without the need to press special keys. ```elixir type(conn, selector, text, opts \\ []) ``` -------------------------------- ### Manually Record and Open Playwright Trace Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This Elixir test snippet uses the `@tag trace: :open` attribute to automatically record a Playwright trace during test execution and open it in the Playwright trace viewer upon completion. This is useful for interactive debugging. ```elixir @tag trace: :open test "record a trace and open it automatically in the viewer" do # test body here end ``` -------------------------------- ### Custom Playwright Feature: Accessibility Audit Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This Elixir function performs an accessibility audit on the current frame using axe-core. It evaluates the accessibility rules, processes the results, and asserts that no violations were found. This requires the 'A11yAudit' library. ```elixir defp assert_a11y(conn) do Frame.evaluate(conn.frame_id, A11yAudit.JS.axe_core()) results = conn.frame_id |> Frame.evaluate("axe.run()") |> A11yAudit.Results.from_json() A11yAudit.Assertions.assert_no_violations(results) conn end ``` -------------------------------- ### Helper Function to Interact Within an Iframe Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This Elixir function provides a convenient way to execute operations within an iframe. It takes an optional iframe selector (defaulting to 'iframe') and a function to execute. It uses `within` to enter the frame context before executing the provided function. ```elixir defp within_iframe(selector \ "iframe", fun) when is_function(fun, 1) do within("#{selector} >> internal:control=enter-frame", fun) end ``` -------------------------------- ### Unwrap Playwright IDs with PhoenixTest Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest The `unwrap` function allows invoking a provided function with Playwright IDs (context, page, frame). This is useful for interacting directly with Playwright's underlying objects for advanced operations. The function takes a Playwright session and a callback function. ```elixir @spec unwrap(t(), (%{context_id: any(), page_id: any(), frame_id: any()} -> any())) :: t() |> unwrap(&Frame.evaluate(&1.frame_id, "console.log('Hey')")) ``` -------------------------------- ### Assert LiveComponent Connection with Playwright Source: https://hexdocs.pm/phoenix_test_playwright/0.8.0/phoenix_test_playwright/PhoenixTest This snippet demonstrates how to assert that a LiveComponent has connected using Playwright. It checks for the presence of a 'data-connected' attribute on a specific element. This is useful for ensuring dynamic content loaded by LiveComponents is available before proceeding with further interactions. ```elixir