Files
EventSnap/e2e/playwright.config.ts
fabi a3c0082c4b test(e2e): update suite for shipped features + gate Chromium-only perms
The running test container had been built from an older tree; rebuilding it to
pick up current app code surfaced several tests that predated shipped
security/UX features:

- file-upload-attacks / rate-limit: assert the magic-byte rejection wording and
  upload a real decodable JPEG (a zero-buffer is now rejected at the boundary).
- ddos: open SSE via the single-use /stream/ticket flow, not the dead ?token=.
- recover-page / join: the PIN field auto-submits on the 4th digit, so don't
  race an explicit submit click against the ensuing navigation.
- gestures-doubletap / sheet-escape: target the lightbox by aria-labelledby and
  anchor the radio accessible-name match at the start (gated dialog semantics).

playwright.config: camera/mic/clipboard are Chromium-only permissions (they threw
"Unknown permission: camera" on firefox/webkit and failed the test at context
creation); grant them per-Chromium-project. firefox-android drops the
unsupported isMobile flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 22:12:05 +02:00

152 lines
5.6 KiB
TypeScript

import { defineConfig, devices } from '@playwright/test';
/**
* EventSnap E2E config.
*
* Defaults assume the test stack is already up (`npm run stack:up`). The
* stack is *not* started by webServer because Playwright would clobber the
* compose volumes between retries. CI calls `npm run stack:up` once before
* `npm run test:e2e` runs.
*
* UA matrix: the `@smoke` happy-path runs across every project to catch
* engine-level divergences. The rest of the suite only runs against
* `chromium-desktop` to keep the wall-clock reasonable.
*/
// camera/microphone/clipboard are Chromium-only permissions; passing them to
// firefox/webkit projects throws "Unknown permission: camera" and fails the
// whole test before it runs. Granted per-Chromium-project below instead of in
// the global `use` block.
const CHROMIUM_PERMISSIONS = ['camera', 'microphone', 'clipboard-read', 'clipboard-write'];
export default defineConfig({
testDir: './specs',
outputDir: './test-results',
fullyParallel: false, // Single shared backend → tests TRUNCATE between, so don't run in parallel.
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1, // One worker. Multi-worker needs per-worker isolated DBs (Phase 2+).
reporter: [
['list'],
['html', { open: 'never', outputFolder: './playwright-report' }],
],
globalSetup: './global-setup.ts',
globalTeardown: './global-teardown.ts',
timeout: 60_000,
expect: { timeout: 10_000 },
use: {
baseURL: process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
actionTimeout: 10_000,
navigationTimeout: 30_000,
// No camera/mic/clipboard here — those are Chromium-only and are granted on
// the Chromium projects below (see CHROMIUM_PERMISSIONS).
},
projects: [
// ── Baseline desktop ─────────────────────────────────────────────────
{
name: 'chromium-desktop',
// 09-mobile/ specs only run on the chromium-mobile project below — they
// require `hasTouch: true` and a phone viewport.
testIgnore: ['**/09-mobile/**'],
use: {
...devices['Desktop Chrome'],
permissions: CHROMIUM_PERMISSIONS,
launchOptions: {
args: [
'--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream',
// A video file would be loaded with --use-file-for-fake-video-capture
// — we ship a tiny .y4m in fixtures/media/ as a follow-up.
],
},
},
},
// ── Phase 3 mobile gestures ──────────────────────────────────────────
// Runs ONLY the 09-mobile/ specs. Uses the Pixel 7 device descriptor so
// hasTouch + isMobile + mobile viewport are all set, then dispatches
// pointer events via page.mouse / locator helpers. Real touch events
// come from `page.touchscreen` when needed.
{
name: 'chromium-mobile',
testMatch: ['**/09-mobile/**/*.spec.ts'],
use: { ...devices['Pixel 7'], permissions: CHROMIUM_PERMISSIONS },
},
// ── Mobile UA smoke matrix (runs only @smoke specs in CI) ────────────
{
name: 'chromium-pixel7',
use: { ...devices['Pixel 7'], permissions: CHROMIUM_PERMISSIONS },
grep: /@smoke/,
},
{
name: 'chromium-galaxy-s22',
use: {
...devices['Galaxy S9+'], // Playwright doesn't ship S22 yet — S9+ is the closest Samsung descriptor.
viewport: { width: 360, height: 780 },
userAgent:
'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36',
permissions: CHROMIUM_PERMISSIONS,
},
grep: /@smoke/,
},
{
name: 'samsung-internet',
use: {
...devices['Galaxy S9+'],
viewport: { width: 360, height: 780 },
userAgent:
'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/24.0 Chrome/124.0.0.0 Mobile Safari/537.36',
permissions: CHROMIUM_PERMISSIONS,
},
grep: /@smoke/,
},
{
name: 'edge-android',
use: {
...devices['Pixel 7'],
userAgent:
'Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.0.0',
permissions: CHROMIUM_PERMISSIONS,
},
grep: /@smoke/,
},
{
name: 'chrome-ios',
use: {
...devices['iPhone 14 Pro'],
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/124.0.0.0 Mobile/15E148 Safari/604.1',
},
grep: /@smoke/,
},
{
name: 'webkit-iphone',
use: { ...devices['iPhone 14 Pro'] },
grep: /@smoke/,
},
{
name: 'firefox-android',
use: {
...devices['Pixel 7'],
defaultBrowserType: 'firefox',
// Firefox rejects `isMobile` (Chromium-only). Keep the phone viewport +
// Android UA for coverage, but drop the unsupported flag.
isMobile: false,
userAgent:
'Mozilla/5.0 (Android 14; Mobile; rv:124.0) Gecko/124.0 Firefox/124.0',
},
grep: /@smoke/,
},
{
name: 'firefox-desktop',
use: { ...devices['Desktop Firefox'] },
grep: /@smoke/,
},
],
});