Fluffy
Phoenix feature tests. Three drivers, one Playwright-shaped API.
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
- Installation and runtime — endpoint, browser, and Ecto sandbox setup
- Usage — locators, actions, expectations, backend choice, and diagnostics
- Assertion styles — imported
assert/refuteandexpectvocabularies compared - Advanced events and pages — tabs, windows, iframe limitations, downloads, navigation, dialogs, and network events
- Coming from PhoenixTest — an alternate starting point for existing PhoenixTest suites
- Capability matrix — backend differences and limitations
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.