Current section
Files
Jump to
Current section
Files
README.md
# Fluffy

**Phoenix feature tests. Three drivers, one Playwright-shaped API.**
[](https://hex.pm/packages/fluffy)
[](https://fluffy.hexdocs.pm/)
[](https://github.com/ftes/fluffy/actions/workflows/ci.yml)
[](LICENSE.md)
Fluffy runs tests through ConnTest, LiveViewTest, or a real browser. Its
in-process drivers are checked against Playwright. Driver differences are
documented in the [capability matrix](docs/capabilities.md).
**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.
[Start with the migration guide →](docs/migration-from-phoenix-test.md)
```elixir
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.
<details>
<summary>HTML behind this example</summary>
<pre>
<code class="language-html">
<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>
</code>
</pre>
</details>
## Getting started
Follow [Installation and runtime](docs/installation.md) for the dependency,
endpoint, browser, and sandbox configuration.
For the native locator API, establish a lifecycle scope and import its helpers:
```elixir
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](docs/installation.md) for Playwright and Ecto sandbox
setup, then [Usage](docs/usage.md) 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:
```elixir
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](docs/migration-from-phoenix-test.md)
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](docs/assertion-styles.md):
`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](docs/advanced-events.md)
for examples and the [Capability matrix](docs/capabilities.md) for what each
backend supports.
## Guides
- [Installation and runtime](docs/installation.md) — endpoint, browser, and
Ecto sandbox setup
- [Usage](docs/usage.md) — locators, actions, expectations, backend choice, and
diagnostics
- [Assertion styles](docs/assertion-styles.md) — imported `assert`/`refute` and
`expect` vocabularies compared
- [Advanced events and pages](docs/advanced-events.md) — tabs, windows, iframe
limitations, downloads, navigation, dialogs, and network events
- [Coming from PhoenixTest](docs/migration-from-phoenix-test.md) — an alternate
starting point for existing PhoenixTest suites
- [Capability matrix](docs/capabilities.md) — 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:
```bash
mix setup
mix test
```
Run the ordinary local gate—Styler-backed formatting, warnings-as-errors
compilation, strict Credo, and the test suite—with:
```bash
mix check
```
Add test-environment Dialyzer analysis with:
```bash
mix quality
```
## License
Fluffy is released under the [MIT License](LICENSE.md).