Browser-level tests driven by Playwright. They exercise the critical user journeys end to end — through real HTTP, the rendered React UI, the database, and the dev mailbox.
They live under assets/ so they reuse the existing package.json, Node
toolchain, and TypeScript setup (assets/tsconfig.e2e.json) rather than a
separate project.
| Spec | Flow |
|---|---|
registration.spec.ts |
Sign up → confirm via the emailed link → land on the dashboard |
login.spec.ts |
Log in; reject bad credentials; an unconfirmed login re-sends the confirmation link instead of starting a session |
logout.spec.ts |
Log out from the header menu → protected pages bounce to /login |
resend-confirmation.spec.ts |
Unconfirmed user requests a fresh link from /login → confirms |
email-confirmation.spec.ts |
Re-clicking a used link keeps an already-confirmed user on the dashboard; a stale link signed out → resend page |
reset-password.spec.ts |
Request a reset link → set a new password → log in with it |
change-email.spec.ts |
Change email → confirm via the link to the new inbox → log in with it (+ wrong-password rejection) |
change-password.spec.ts |
Change password → old one fails, new one works (+ wrong-password rejection) |
delete-account.spec.ts |
Delete the account behind a password-confirm dialog → can no longer sign in (+ wrong-password rejection) |
auth-guards.spec.ts |
Pipeline redirects: anonymous → /login, already signed-in → /dashboard |
locale.spec.ts |
Switch the interface language (English → Spanish) |
-
The toolchain (Erlang, Elixir, Node) installed via mise — see the README.
-
The Playwright browser, installed once:
npm --prefix assets run e2e:install
The suite drives a running dev server — it does not boot one. Start the server in one terminal:
mix phx.serverThen run the suite in another:
npm --prefix assets run e2e # headless
npm --prefix assets run e2e:ui # Playwright UI mode (watch + time travel)
npm --prefix assets run e2e:headed # headed browserPoint the suite at a different server with E2E_BASE_URL (defaults to
http://localhost:4000).
The suite runs fullyParallel. Every test that creates data generates a
unique e2e-test-…@example.com email (uniqueEmail/1 in helpers.ts), so
parallel workers never collide on the unique email index. Each test also
runs in its own browser context, so cookies (locale, session) don't leak
between tests.
Auth rate limiting is disabled in dev (config/dev.exs), since the
suite drives the dev server and makes many auth requests from one IP. The
limiter is a production concern and is covered separately by
rate_limit_test.exs.
Tests that need an existing account call the dev-only fixture endpoint
POST /dev/e2e/users, which mints a confirmed user and skips the
email-confirmation round-trip. The registration spec is the exception — it
drives the real sign-up and clicks the confirmation link.
Before the suite runs, global-setup.ts executes priv/repo/e2e.exs,
which deletes per-run users left over from earlier runs (deleting a user
cascades to their tokens), so every run starts clean.
Both the endpoint and the cleanup script are gated to :dev / :test
with :dev_routes enabled, and only ever touch e2e-test-… accounts —
they are unreachable in production and cannot affect real data.
Authentication here is link-based, so the link-driven specs (sign-up,
password reset, email change, resend confirmation) read the single-click
URL out of the dev mailbox JSON (/dev/mailbox/json) via
fetchEmailLink/3 and navigate to it.
- Add a
*.spec.tsunderassets/e2e/tests/. - Generate any users with
uniqueEmail/1orprovisionUser/2fromhelpers.ts— never hard-code an email, or parallel runs will clash. - If the flow needs an authenticated session, prefer
provisionUserover walking the whole sign-up UI; reserve the full journey for the spec that's actually testing it.
assets/
playwright.config.ts # base URL, parallelism, global setup
tsconfig.e2e.json # node-typed TS project for the suite
e2e/
global-setup.ts # runs priv/repo/e2e.exs before the suite
helpers.ts # uniqueEmail, provisionUser, loginAs, logoutViaMenu, gotoSettingsViaMenu, fetchEmailLink
tests/ # one spec per flow
priv/repo/e2e.exs # destructive per-run cleanup (dev/test only)
lib/elixir_react_starter_web/controllers/dev_e2e_controller.ex # POST /dev/e2e/users