Fluffy offers two imported vocabularies for the same assertions. The guides use
assert and refute; choose expect if you prefer Playwright-style names.
Both construct the same expectation values and preserve driver behavior,
retries, timeout options, diagnostics, and the returned session.
ExUnit style
Place use Fluffy.Assert after use ExUnit.Case or your application's case
module. It imports the constructors and replaces only ExUnit's conflicting
assert/2 and refute/2 imports. Ordinary ExUnit assertions, pattern bindings,
custom messages, and other helpers remain available.
use ExUnit.Case, async: true
use Fluffy.Assert
import Fluffy
import Fluffy.Locator
setup context do
Fluffy.Test.setup(context)
end
test "registers a creature" do
start_session(:phoenix)
|> visit("/creatures/new")
|> fill(by_label("Name"), "Basilisk")
|> click(by_role(:button, name: "Register"))
|> assert(visible(by_text("Creature registered")))
|> refute(page_title("Error"))
|> assert(page_url("/creatures/basilisk"))
assert {:ok, name} = {:ok, "Basilisk"}
assert name == "Basilisk", "ordinary ExUnit assertions still work"
endSee Shared test case to centralize these imports and lifecycle setup.
Expect style
Use import Fluffy.Expect in place of use Fluffy.Assert. Actions and locators
keep their names; assertion constructors use to_be_* and to_have_*.
use ExUnit.Case, async: true
import Fluffy
import Fluffy.Expect
import Fluffy.Locator
setup context do
Fluffy.Test.setup(context)
end
test "registers a creature" do
start_session(:phoenix)
|> visit("/creatures/new")
|> fill(by_label("Name"), "Basilisk")
|> click(by_role(:button, name: "Register"))
|> expect(to_be_visible(by_text("Creature registered")))
|> expect(not_(page_to_have_title("Error")))
|> expect(page_to_have_url("/creatures/basilisk"))
endNaming comparison
The following calls are pipeline steps, with the session supplied by |>.
Locator constructors take a locator, page constructors target the active page,
and captured-result constructors take the capture key first.
| ExUnit style | Expect style |
|---|---|
assert(visible(locator)) | expect(to_be_visible(locator)) |
refute(visible(locator)) | expect(not_(to_be_visible(locator))) |
assert(checked(locator)) | expect(to_be_checked(locator)) |
assert(enabled(locator)) | expect(to_be_enabled(locator)) |
assert(count(locator, 3)) | expect(to_have_count(locator, 3)) |
assert(value(locator, "Basilisk")) | expect(to_have_value(locator, "Basilisk")) |
assert(page_url("/creatures")) | expect(page_to_have_url("/creatures")) |
assert(page_title("Creatures")) | expect(page_to_have_title("Creatures")) |
assert(download_suggested_filename(:report, "report.csv")) | expect(download_to_have_suggested_filename(:report, "report.csv")) |
assert(dialog_message(:confirmation, "Saved")) | expect(dialog_to_have_message(:confirmation, "Saved")) |
assert(navigation_url(:destination, path: "/creatures")) | expect(navigation_to_have_url(:destination, path: "/creatures")) |
assert(request_method(:save, "POST")) | expect(request_to_have_method(:save, "POST")) |
assert(response_status(:save, 201)) | expect(response_to_have_status(:save, 201)) |
The same naming pattern applies to the remaining constructors. See the
Fluffy.Assert and Fluffy.Expect references for their complete APIs.
Options and negation
Options can go on the constructor or execution call. Execution options take precedence, and both forms return the updated session:
session
|> assert(visible(by_text("Creature registered"), timeout: 1_000))
|> refute(visible(by_text("Saving…")), timeout: 1_000)refute(session, expectation) executes the negated expectation, just like
expect(session, not_(expectation)). It preserves driver errors rather than
turning them into successful refutations. Retrying drivers wait until the
negative condition holds; they do not require it to hold for the entire timeout.
Captured-result assertions inspect an already retained result immediately;
their timeout does not wait for a new event. Capture that event with wait_for.
Refuting visibility accepts an absent or invisible element. See Visibility and DOM presence for absence checks, hidden elements, and the structural versus rendered visibility rules. Choosing a vocabulary does not change those driver boundaries.