Open Finapp
Development

Testing

Unit and E2E testing scripts.

Unit Tests

Run Vitest for unit tests:

Terminal
pnpm test

E2E Tests

E2E tests run with Playwright. Playwright ships browsers separately from its npm package, so a one-time install is required after cloning.

Scripts

ScriptPurpose
pnpm test:e2e:installOne-time install of the Chromium binary. Re-run after upgrading @playwright/test (browser version is pinned to the package version).
pnpm test:e2eRun all tests headless. Use this in CI.
pnpm test:e2e:headedRun tests with a visible browser window. Useful for watching a flow or debugging a flaky test.
pnpm test:e2e:uiOpen Playwright's interactive UI runner with time-travel debugging, watch mode, and step-by-step inspection. Best for writing new tests.
pnpm test:e2e:reportOpen the HTML report from the last run (screenshots, traces, videos of failures).

Typical Workflow

  1. First time only: pnpm test:e2e:install
  2. Writing tests: pnpm test:e2e:ui
  3. CI / full sweep: pnpm test:e2e
  4. Investigating a failure: pnpm test:e2e:report

Only test:e2e and test:e2e:install are strictly required - the rest are convenience wrappers around playwright CLI flags.

Local Auth Without Google (Seed Data)

Real-mode (PowerSync) testing needs a signed-in session, but Google OAuth can't be automated. The local stack ships a fixed email/password test user plus a demo-derived dataset, so agents / Playwright / manual runs can enter real mode without Google.

Email/password is enabled in the Supabase backend but intentionally not exposed in the app UI (Google + Demo only). The seed user exists for local tests; its password is local-only and not a secret.

Seed

app/supabase/seed.sql (generated, idempotent, transactional) creates:

  • a confirmed test user - e2e@finapp.local / finapp-e2e (fixed UUID), and
  • its demo dataset: 8 wallets, 32 categories, ~860 transactions.

It is applied automatically by supabase db reset ([db.seed] in app/supabase/config.toml). To (re)apply against a running DB without a full reset:

Terminal
docker exec -i supabase_db_app psql -U postgres -d postgres < app/supabase/seed.sql

The seed user's email/password works via the Supabase password grant even with email signup disabled ([auth.email] enable_signup = false), because the user is pre-created. The GoTrue token columns are seeded as '' (not NULL) - a NULL triggers Database error querying schema on sign-in.

Regenerating the seed from demo data

seed.sql is produced from a snapshot of the real demo generator by app/scripts/gen-seed.mjs:

  1. Open the app in demo mode, read the Pinia stores via devtools, and save { wallets, categories, trns } to app/scripts/.demo-snapshot.json (gitignored).
  2. node app/scripts/gen-seed.mjs - writes app/supabase/seed.sql.

Synthetic adjustment / transfer categories are skipped (they are trns.categoryId literals, not real category rows).

Signing in as the test user

app/scripts/dev-login.mjs mints a session via the password grant and prints the value for localStorage['finapp.auth']:

Terminal
node app/scripts/dev-login.mjs --raw    # prints the raw session JSON

In the browser, set that value, clear the finapp.isDemo cookie, and reload - the app boots into real PowerSync mode as the test user and the seeded data streams in. Credentials/URL are read from app/.env (overridable via E2E_EMAIL / E2E_PASSWORD).

This is also the basis for a Playwright storageState global-setup: sign in once via the grant, then reuse the session across tests.