Fluffy

A friendly three-headed Fluffy asleep beside an Elixir wizard playing a flute, with the heads labeled Live, Static, and Playwright

Phoenix feature tests. Three drivers, one Playwright-shaped API.

Hex.pm HexDocs CI License

Fluffy tests Phoenix pages and LiveViews directly, or uses a real browser when needed. Fluffy's behavior is checked against a real browser to verify that tests run without a browser behave consistently. See the capability matrix for supported behavior and differences.

Coming from PhoenixTest? Use import Fluffy.PhoenixTest to keep familiar helpers such as fill_in, click_button, and assert_has. The facade makes Fluffy mostly a drop-in replacement for supported tests, including browser tests. Get automatic waiting for LiveView actions and assertions, plus automatic LiveView connection waiting in browser tests. Start with the migration guide →

creature_row =
  by_role(:table, name: "Hagrid's creatures")
  |> by_role(:row)
  |> filter(has: by_text("Fluffy", exact: true))

start_session(:phoenix)
|> visit("/creatures")
|> click(by_role(creature_row, :button, name: "Play flute"))
|> assert(visible(by_role(creature_row, :cell, name: "Asleep")))

This example assumes the imports and test setup below. Locators are reusable queries: find Fluffy's row in the “Hagrid's creatures” table and scope both the action and the assertion to it.

HTML behind this example

<h1 id="guards-heading">Hagrid's creatures</h1>
<table aria-labelledby="guards-heading">
  <thead>
    <tr><th>Creature</th><th>State</th><th>Actions</th></tr>
  </thead>
  <tbody>
    <tr>
      <td>Norbert</td>
      <td>Awake</td>
      <td>
        <form method="post" action="/creatures">
          <input type="hidden" name="creature" value="norbert">
          <button name="action" value="feed">Feed dragon</button>
        </form>
      </td>
    </tr>
    <tr>
      <td>Fluffy</td>
      <td>Awake</td>
      <td>
        <form method="post" action="/creatures">
          <input type="hidden" name="creature" value="fluffy">
          <button name="action" value="flute">Play flute</button>
        </form>
      </td>
    </tr>
  </tbody>
</table>

Getting started

Follow Installation and runtime for the dependency, endpoint, browser, and sandbox configuration.

For the native locator API, establish a lifecycle scope and import its helpers:

use ExUnit.Case, async: true
use Fluffy.Assert

import Fluffy
import Fluffy.Locator

setup context do
  Fluffy.Test.setup(context)
end

Start with start_session(:phoenix) for in-process Static and LiveView tests. Use start_session(:playwright) when a test needs a real browser. See Installation and runtime for Playwright and Ecto sandbox setup, then Usage for writing tests and a shared FluffyCase.

Coming from PhoenixTest

Replace import PhoenixTest with import Fluffy.PhoenixTest and add Fluffy.Test.setup/1 to your test setup. Existing pipelines can keep their helper names and prepared connections:

import Fluffy.PhoenixTest

# Inside a test with Fluffy lifecycle setup:
conn
|> visit("/creatures/new")
|> fill_in("Name", with: "Basilisk")
|> click_button("Register")
|> assert_has("#notice", text: "Creature registered")
|> assert_path("/creatures")

Use the facade import on its own. The migration guide shows complete setup, the same helpers with Playwright, and compatibility boundaries. Adopting the native locator API is optional.

For native tests, choose an assertion vocabulary: use Fluffy.Assert or import Fluffy.Expect.

Beyond page interactions

Capture downloads, open new tabs, handle dialogs, and observe network events with Fluffy's event API. Register a wait before an action, then await a value. Predicates select events; ordinary ExUnit assertions inspect their metadata. See Advanced events and pages for examples and the Capability matrix for what each backend supports.

Guides

Developing Fluffy

The project pins the current local-development toolchain in .tool-versions. Fluffy's compatibility floor remains Elixir 1.18 and Node.js 20; development also requires pnpm 11.19.0 and PostgreSQL. From a clean checkout:

mix setup
mix test

Run the ordinary local gate—Styler-backed formatting, warnings-as-errors compilation, strict Credo, and the test suite—with:

mix check

Add test-environment Dialyzer analysis with:

mix quality

License

Fluffy is released under the MIT License.