mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
055c3ae5c7
The pictures picked up five faults that the run could not see, because a passing step only proves the click landed. The ring vanished on the lightbox: the accent is nearly black and so is the overlay. It now measures the surface behind it and puts a white hairline outside itself and the number only where the contrast is too low, so every other picture keeps the single line it had. The number fell outside the picture whenever the target was wider than the frame, which is every full-width row: the frame is known before the ring is drawn, so the number moves inside far enough to clear the edge. The satellite imagery was a grey rectangle although every tile came back 200. Leaflet fades a tile in over 200 ms and takes the progress from `+new Date()`, and the browser's clock is pinned to the picture day, so the opacity it writes on every frame is always 0. A stylesheet on Leaflet's own `leaflet-tile-loaded` outranks that inline style. The satellite guide also waits for decoded tiles now instead of for a tile element to exist. The uploaded files looked like nothing anyone would upload. A PDF is drawn with an issuer band, a reference, a table of fields and a footer, and the two photographs are gone: a drawn photograph reads as clip art at any size, so the trip carries a map screenshot with a pin and a booking confirmation in a browser window, which are graphics in reality too. In the panel, a step picture was covered into a 16:10 box and lost its sides; contained everywhere now, the hero and the result in 16:9. Beside that: the place guide gained the drag-and-drop route to a day as its fourth step, in all 23 languages, and `e2e/help/README.md` now holds the whole visual contract, the traps that cost this round (the pinned clock, prettier against the client sources, a killed run holding its ports) and how to find out which pictures a change forces to be retaken.
136 lines
5.4 KiB
TypeScript
136 lines
5.4 KiB
TypeScript
import { defineConfig, devices } from '@playwright/test'
|
|
|
|
/**
|
|
* E2E harness for TREK's critical user flows (FE7).
|
|
*
|
|
* Two web servers are orchestrated: the Express/Nest backend on :3001 against an
|
|
* isolated throwaway SQLite DB (e2e/server-launch.mjs sets TREK_DB_FILE + seeds a
|
|
* known admin), and the Vite dev server on :5173 which proxies /api, /uploads,
|
|
* /ws to the backend. Tests run serially against one worker so they share the
|
|
* single seeded database deterministically.
|
|
*/
|
|
/**
|
|
* Ports. The defaults are what `npm run e2e` has always used. `npm run
|
|
* help:media` (e2e/help/run.mjs) sets E2E_WEB_PORT/E2E_API_PORT to a second
|
|
* pair so it can record while `npm run dev` keeps serving 5173/3001 to whoever
|
|
* is watching. Environment only, no argv sniffing: Playwright's workers load
|
|
* this file again with different arguments, and every process must agree.
|
|
* vite.config.js honours TREK_DEV_PORT and TREK_DEV_API for the same reason.
|
|
*/
|
|
const WEB_PORT = Number(process.env.E2E_WEB_PORT) || 5173
|
|
const API_PORT = Number(process.env.E2E_API_PORT) || 3001
|
|
export const E2E_BASE_URL = `http://localhost:${WEB_PORT}`
|
|
/**
|
|
* The session and the seed's ids belong to the database the run boots, which is
|
|
* named after its API port (e2e/server-launch.mjs). Two runs on two port pairs
|
|
* therefore share nothing, and the default port keeps the old file names.
|
|
*/
|
|
export const E2E_STATE_FILE = `e2e/.tmp/state-${API_PORT}.json`
|
|
export const E2E_SEED_FILE = `e2e/.tmp/seed-${API_PORT}.json`
|
|
|
|
// The day the seed and the pictures are relative to (e2e/dates.ts). Fixed here
|
|
// for every project and worker that loads this file, so a run that crosses
|
|
// midnight still agrees with itself; `help:media` sets it before this runs.
|
|
if (!process.env.E2E_PICTURE_DAY) {
|
|
const d = new Date()
|
|
const pad = (n: number) => String(n).padStart(2, '0')
|
|
process.env.E2E_PICTURE_DAY = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
|
|
}
|
|
|
|
export default defineConfig({
|
|
testDir: './e2e',
|
|
fullyParallel: false,
|
|
workers: 1,
|
|
forbidOnly: !!process.env.CI,
|
|
retries: process.env.CI ? 1 : 0,
|
|
timeout: 45_000,
|
|
expect: { timeout: 15_000 },
|
|
reporter: [['list']],
|
|
use: {
|
|
baseURL: E2E_BASE_URL,
|
|
trace: 'on-first-retry',
|
|
screenshot: 'only-on-failure',
|
|
},
|
|
projects: [
|
|
// Unauthenticated flows (login, register, public share) — no stored session.
|
|
{ name: 'public', testMatch: /\.public\.spec\.ts/, use: { ...devices['Desktop Chrome'] } },
|
|
// One-time login that persists a session for the authenticated flows.
|
|
{ name: 'setup', testMatch: /auth\.setup\.ts/ },
|
|
{
|
|
name: 'app',
|
|
testMatch: /\.spec\.ts/,
|
|
testIgnore: /(\.public\.spec\.ts|auth\.setup\.ts)/,
|
|
use: { ...devices['Desktop Chrome'], storageState: E2E_STATE_FILE },
|
|
dependencies: ['setup'],
|
|
},
|
|
// Documentation screenshots (`npm run shots`). Excluded from the normal e2e
|
|
// run by its own testMatch — these capture artwork for wiki/assets/, they
|
|
// assert nothing. 2x scale keeps text crisp at the sizes the wiki renders.
|
|
// Populates the demo trip the screenshots are taken of. Separate project so
|
|
// it runs exactly once, between auth and capture.
|
|
{
|
|
name: 'seed',
|
|
testMatch: /seed\.setup\.ts/,
|
|
use: { ...devices['Desktop Chrome'], storageState: E2E_STATE_FILE },
|
|
dependencies: ['setup'],
|
|
},
|
|
{
|
|
name: 'screenshots',
|
|
testMatch: /\.shot\.ts/,
|
|
use: {
|
|
...devices['Desktop Chrome'],
|
|
storageState: E2E_STATE_FILE,
|
|
viewport: { width: 1440, height: 900 },
|
|
deviceScaleFactor: 2,
|
|
},
|
|
dependencies: ['seed'],
|
|
},
|
|
// Help-center media (`npm run help:media`): pictures and walkthroughs for
|
|
// the in-app guides, generated from the guide definitions in src/help/.
|
|
// Same seed as the wiki screenshots; its own match so neither run pays
|
|
// for the other. Serial and generous on time: a walkthrough is paced for
|
|
// a human to follow.
|
|
{
|
|
name: 'help-media',
|
|
testMatch: /\.guide\.ts/,
|
|
// Four minutes. A guide is a whole task, and a few of them wait on the
|
|
// network more than once: the nearby-places one runs two POI searches
|
|
// against a public index and opens the place form on top of them, which
|
|
// does not fit in the two and a half minutes this used to allow.
|
|
timeout: 240_000,
|
|
use: {
|
|
...devices['Desktop Chrome'],
|
|
// The full Chrome for Testing build rather than the headless shell the
|
|
// other projects run on. Only the full build carries the PDF viewer,
|
|
// and the Files tab's preview embeds a PDF as <object>, which the
|
|
// shell leaves as a "Download PDF" link. Still headless.
|
|
channel: 'chromium',
|
|
storageState: E2E_STATE_FILE,
|
|
viewport: { width: 1920, height: 1080 },
|
|
deviceScaleFactor: 2,
|
|
},
|
|
dependencies: ['seed'],
|
|
},
|
|
],
|
|
webServer: [
|
|
{
|
|
// Always start our own backend (never reuse) so the isolated test DB is
|
|
// reset + reseeded on every run, regardless of any stray dev server.
|
|
command: 'node e2e/server-launch.mjs',
|
|
port: API_PORT,
|
|
env: { E2E_API_PORT: String(API_PORT) },
|
|
reuseExistingServer: false,
|
|
timeout: 180_000,
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
{
|
|
command: 'npm run dev',
|
|
port: WEB_PORT,
|
|
env: { TREK_DEV_PORT: String(WEB_PORT), TREK_DEV_API: `http://localhost:${API_PORT}` },
|
|
reuseExistingServer: !process.env.CI,
|
|
timeout: 120_000,
|
|
},
|
|
],
|
|
})
|