tests/{unit,integration,e2e}/, at the repo root (not under frontend/,
since e2e exercises the frontend and PocketBase together) — all
against real code rather than mocks:
- unit/: Vitest (nuxt environment) — pure functions in server/utils
and shared/utils, plus components/composables via
@nuxt/test-utils/runtime's mountSuspended.
- integration/: Vitest (node environment) — @nuxt/test-utils/e2e's
setup() builds and runs the real Nitro server against a real
ephemeral PocketBase instance, exercising the actual /content/* and
/admin routes end to end.
- e2e/: Playwright, a real browser against the built app and another
ephemeral PocketBase, covering the user-facing flows (home, blog
archive, article detail incl. JSON-LD, category filter, 404s, the
/admin redirect).
The Vitest/Playwright tooling (and node_modules) stays in frontend/,
the repo's only npm project; its configs just point at ../tests/.
Playwright resolves packages from the node_modules nearest each spec
file, so `npm run test:e2e` first symlinks tests/node_modules to
frontend/node_modules (pretest:e2e, idempotent, gitignored).
tests/support/pocketbase.ts is the shared piece behind integration
and e2e: it downloads the same pinned PocketBase binary
pocketbase/Dockerfile uses, starts it against the real
pocketbase/pb_migrations/ (not a copy), and seeds it from
tests/support/fixtures.ts — so these tests run against the exact
schema and API rules that ship to production, catching the class of
bug that only shows up when PocketBase actually enforces them (e.g. a
wrong filter/fields query string silently returning nothing, or too
much).
CLAUDE.md and docs/frontend.md document the new npm scripts and the
single-test commands for each layer.
36 lines
1.2 KiB
TypeScript
36 lines
1.2 KiB
TypeScript
import { fileURLToPath } from 'node:url'
|
|
import { defineConfig, devices } from '@playwright/test'
|
|
|
|
// Fixed ports so the PocketBase instance (started in global-setup.ts) and the
|
|
// built Nuxt server (started by Playwright's webServer below) can reference
|
|
// each other statically — see tests/e2e/global-setup.ts for why.
|
|
export const E2E_POCKETBASE_PORT = 8095
|
|
const APP_PORT = 3100
|
|
const BASE_URL = `http://127.0.0.1:${APP_PORT}`
|
|
const POCKETBASE_URL = `http://127.0.0.1:${E2E_POCKETBASE_PORT}`
|
|
|
|
export default defineConfig({
|
|
testDir: '../tests/e2e',
|
|
fullyParallel: false,
|
|
retries: 0,
|
|
reporter: [['list']],
|
|
use: {
|
|
baseURL: BASE_URL,
|
|
},
|
|
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
|
|
globalSetup: fileURLToPath(new URL('../tests/e2e/global-setup.ts', import.meta.url)),
|
|
webServer: {
|
|
command: 'npm run build && node .output/server/index.mjs',
|
|
url: BASE_URL,
|
|
reuseExistingServer: false,
|
|
timeout: 120000,
|
|
env: {
|
|
HOST: '127.0.0.1',
|
|
PORT: String(APP_PORT),
|
|
NUXT_POCKETBASE_URL: POCKETBASE_URL,
|
|
NUXT_PUBLIC_POCKETBASE_URL: POCKETBASE_URL,
|
|
NUXT_PUBLIC_SITE_URL: BASE_URL,
|
|
},
|
|
},
|
|
})
|