UAT (residential partners must have zero access to anything non-residential; no marina dashboard). Server-side their permission map already 403s every marina domain — this locks the client surface to match: - AppShell: a residential-only user (residential_clients.view && !clients.view, non-super-admin) is redirected off ANY non-residential route to /residential/clients. Blocks the marina dashboard + every marina page in one place; personal surfaces (settings, inbox) stay reachable. (Fixes F4 — they no longer land on a marina dashboard of 403-ing empty widgets.) - Mobile bottom tabs were hardcoded Dashboard/Clients/Berths regardless of role; now role-aware — residential-only users get Residential Clients/Interests instead of marina tabs they 403 on. (Fixes F5.) - e2e: stale `#email` login selector → `#identifier` (smoke helper) — a real reason the smoke auth specs fail independent of the dev-server OOM. - New crash-safe `matrix` Playwright project (role×viewport access matrix + responsive overflow sweep) — lean alternative to the full suite which OOM-crashes next dev locally. Verified: matrix run shows residential_partner redirected to residential + residential-scoped mobile tabs; 403s unchanged; tsc + eslint + 42 permission tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
123 lines
3.7 KiB
TypeScript
123 lines
3.7 KiB
TypeScript
import { defineConfig, devices } from '@playwright/test';
|
||
|
||
export default defineConfig({
|
||
testDir: './tests/e2e',
|
||
fullyParallel: false,
|
||
forbidOnly: !!process.env.CI,
|
||
retries: 0,
|
||
workers: 1, // Sequential — so tests can build on each other
|
||
reporter: [['list'], ['html', { open: 'never' }]],
|
||
timeout: 60_000,
|
||
expect: { timeout: 10_000 },
|
||
|
||
use: {
|
||
baseURL: 'http://localhost:3000',
|
||
trace: 'on-first-retry',
|
||
screenshot: 'only-on-failure',
|
||
video: 'retain-on-failure',
|
||
actionTimeout: 15_000,
|
||
navigationTimeout: 30_000,
|
||
},
|
||
|
||
projects: [
|
||
{
|
||
name: 'setup',
|
||
testMatch: /smoke\/global-setup\.ts/,
|
||
},
|
||
{
|
||
// Permission-matrix UX sweep. Users + roles are seeded separately via
|
||
// `pnpm tsx tests/e2e/permissions/seed-permission-matrix.ts` (no global
|
||
// setup dependency — relies on the already-seeded dev DB).
|
||
name: 'permissions',
|
||
testMatch: /permissions\/.*\.spec\.ts/,
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1440, height: 900 },
|
||
},
|
||
},
|
||
{
|
||
// Lean role × viewport access matrix. Users pre-seeded (admin/director/
|
||
// sales/viewer/residential_partner) — no global-setup dependency. Few
|
||
// route compilations, so it stays under the dev-server OOM threshold.
|
||
name: 'matrix',
|
||
testMatch: /matrix\/.*\.spec\.ts/,
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1440, height: 900 },
|
||
},
|
||
},
|
||
{
|
||
name: 'smoke',
|
||
testMatch: /smoke\/\d{2}-.*\.spec\.ts/,
|
||
dependencies: ['setup'],
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1440, height: 900 },
|
||
},
|
||
},
|
||
{
|
||
name: 'exhaustive',
|
||
testMatch: /exhaustive\/.*\.spec\.ts/,
|
||
dependencies: ['setup'],
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1440, height: 900 },
|
||
},
|
||
},
|
||
{
|
||
name: 'destructive',
|
||
testMatch: /destructive\/.*\.spec\.ts/,
|
||
dependencies: ['setup'],
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1440, height: 900 },
|
||
},
|
||
},
|
||
{
|
||
// Real-API tests hit live external services (Documenso, IMAP, etc.).
|
||
// Opt-in only: pnpm exec playwright test --project=realapi
|
||
name: 'realapi',
|
||
testMatch: /realapi\/.*\.spec\.ts/,
|
||
dependencies: ['setup'],
|
||
timeout: 120_000,
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1440, height: 900 },
|
||
},
|
||
},
|
||
{
|
||
// Visual regression baselines. Regenerate with --update-snapshots after
|
||
// intentional UI changes; otherwise pnpm exec playwright test --project=visual
|
||
// diffs against the committed PNGs.
|
||
name: 'visual',
|
||
testMatch: /visual\/.*\.spec\.ts/,
|
||
dependencies: ['setup'],
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
viewport: { width: 1440, height: 900 },
|
||
},
|
||
},
|
||
{
|
||
// Mobile / tablet audit — visits every page in headed Chromium at iPhone
|
||
// viewports (portrait), screenshots full-page to .audit/mobile/<viewport>/,
|
||
// and writes an index.md. Depends on `setup` for seeded admin + port-role.
|
||
name: 'mobile-audit',
|
||
testMatch: /audit\/mobile\.spec\.ts/,
|
||
dependencies: ['setup'],
|
||
// Single test walks 4 viewports × ~45 routes sequentially with slowMo;
|
||
// 30 min headroom keeps us well under the wall-clock cost.
|
||
timeout: 1_800_000,
|
||
use: {
|
||
headless: false,
|
||
launchOptions: { slowMo: 200 },
|
||
screenshot: 'off',
|
||
video: 'off',
|
||
trace: 'off',
|
||
},
|
||
},
|
||
],
|
||
|
||
// Don't start the dev server — we expect it to already be running
|
||
webServer: undefined,
|
||
});
|