Set up Playwright tests with authenticated users, test databases, and isolated test contexts
End-to-end tests verify your entire Vertz app — server, database, auth, and UI — from the user’s perspective. This guide covers setting up Playwright, authenticating test users programmatically, and managing test databases.
The webServer option starts your dev server before tests run and shuts it down after. In local development, reuseExistingServer skips startup if the server is already running.
For E2E tests, use a file-based SQLite database. Each dev server run creates its own database file, and tests run against that instance.
// src/api/db.tsimport { Database } from 'bun:sqlite';import { createDb } from '@vertz/db';import { authModels } from '@vertz/server';const sqlite = new Database('./data/app.db');sqlite.exec('PRAGMA journal_mode=WAL');sqlite.exec('PRAGMA foreign_keys=ON');export const db = createDb({ models: { ...authModels, // auth_users, sessions, oauth_accounts, etc. // ... your app models }, dialect: 'sqlite', d1: createBunD1(sqlite), // D1-compatible wrapper});
authModels provides the table definitions Vertz needs for sessions, users, and OAuth accounts. When you call app.initialize(), auth tables are created automatically.
Vertz auth uses HttpOnly cookies, so you can’t set tokens from browser JavaScript. Instead, call the auth endpoints from your test setup and pass the cookies to Playwright’s browser context.
The emailPassword: {} option in createAuth() enables the POST /api/auth/signup and POST /api/auth/signin endpoints. This is the simplest way to create test users programmatically:
Tenant switching requires the tenant.verifyMembership callback in your server config. For testing, you can accept all tenants or check against a seed list.
Use Playwright’s built-in assertions with timeouts instead of page.waitForTimeout():
// Wait for an element to appear after data loadsawait expect(page.getByTestId('project-list')).toBeVisible({ timeout: 10_000 });// Wait for a specific countawait expect(page.getByTestId('project-card')).toHaveCount(3);
bun run e2e # Run all E2E tests (headless)bun run e2e:headed # Run with browser visible (for debugging)bun run e2e -- --ui # Open Playwright's interactive UI
In CI, tests run headless with a single worker for stability. The webServer config ensures the dev server starts fresh.