/** * Runs once before the whole E2E suite. Two jobs: * * 1. Seed a known-good demo workspace deterministically, by shelling out to * the same `backend/scripts/seed_demo.py --reset` a human would run — * see that script's docstring for exactly what it creates (couple * workspace, Alex/Sam/Nova, wishes, overrides, a few applied days). * * 2. Materialize one extra, deterministic multi-week band of planned leave * days via the API (never by clicking through the UI) — the calendar * only renders committed `planned_leave_day` rows, not raw wishes, and * the seed script's own applied days are only 3 days long (too short to * exercise cross-week-row banding). The band's dates are computed * relative to "today", never hardcoded, and written to * `e2e/.auth/fixture.json` for `calendar-band.spec.ts` to read. * * Defensive design: this polls the backend's health endpoint before doing * anything else, regardless of what order Playwright happens to start * `webServer` vs. `globalSetup` in — a setup failure here should always be * "the backend never came up", never a flaky race. */ import { mkdirSync, writeFileSync } from 'node:fs' import path from 'node:path' import { execFileSync } from 'node:child_process' import { fileURLToPath } from 'node:url' import { apiCreatePlannedDay, apiGetContext, apiLogin, DEMO_EMAIL, DEMO_PASSWORD, waitForBackendHealth } from './support/api' import { FIXTURE_PATH } from './support/fixture' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const BACKEND_DIR = path.resolve(__dirname, '../../backend') // A deterministic multi-week band, independent of the leave-wish allocation // engine's own date choices: 15 consecutive WEEKDAYS (~3 calendar-week // rows), starting on the 1st of the month that is two months after // "today" — always safely in the future, always entirely inside one // calendar month (so the Calendar page shows both the band's true start and // true end in a single month view), and never tied to a hardcoded year. function computeBandDates(): { startDate: string; endDate: string; dates: string[] } { const now = new Date() const monthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 2, 1)) const dates: string[] = [] const cursor = new Date(monthStart.getTime()) while (dates.length < 15) { const weekday = cursor.getUTCDay() if (weekday !== 0 && weekday !== 6) { dates.push(cursor.toISOString().slice(0, 10)) } cursor.setUTCDate(cursor.getUTCDate() + 1) } const first = dates[0] const last = dates[dates.length - 1] if (!first || !last) throw new Error('failed to compute band dates') return { startDate: first, endDate: last, dates } } export default async function globalSetup(): Promise { await waitForBackendHealth() execFileSync('uv', ['run', 'python', 'scripts/seed_demo.py', '--reset'], { cwd: BACKEND_DIR, stdio: 'inherit', }) const token = await apiLogin(DEMO_EMAIL, DEMO_PASSWORD) const context = await apiGetContext(token) const child = context.children[0] if (!child) { throw new Error('seed_demo.py did not create a child for the demo workspace') } const band = computeBandDates() for (const date of band.dates) { await apiCreatePlannedDay(token, { workspace_id: context.workspace_id, child_id: child.id, parent_personal_data_uuid: context.personal_data_uuid, date, day_type: 'vacation', is_dubbeldag: false, }) } mkdirSync(path.dirname(FIXTURE_PATH), { recursive: true }) writeFileSync( FIXTURE_PATH, JSON.stringify( { workspaceId: context.workspace_id, childId: child.id, personalDataUuid: context.personal_data_uuid, childBirthDate: child.birth_date, band: { startDate: band.startDate, endDate: band.endDate, parentPersonalDataUuid: context.personal_data_uuid, dayType: 'vacation', }, }, null, 2, ), ) }