Files
umami/tests/api/global-setup.ts
T

83 lines
2.6 KiB
TypeScript

/* eslint-disable no-console */
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { type FullConfig, request } from '@playwright/test';
import { ApiClient } from './client';
import { disableCoverageRecording } from './coverage/recorder';
import { assertDisposableTarget, COVERAGE_DIR, SEED_FILE } from './paths';
import { seedEnvironment } from './seed/setup';
const HEARTBEAT_TIMEOUT_MS = 120_000;
async function waitForHeartbeat(baseURL: string, timeoutMs: number) {
const deadline = Date.now() + timeoutMs;
let lastError = '';
while (Date.now() < deadline) {
try {
const response = await fetch(`${baseURL}/api/heartbeat`);
if (response.ok) {
return;
}
lastError = `HTTP ${response.status}`;
} catch (error) {
lastError = (error as Error).message;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error(
`Timed out waiting for ${baseURL}/api/heartbeat (${lastError}). ` +
'Start the stack with `pnpm test:api:up` or run the full flow with `pnpm test:api`.',
);
}
export default async function globalSetup(config: FullConfig) {
// Seeding traffic must not count towards endpoint coverage.
disableCoverageRecording();
const baseURL = config.projects[0]?.use?.baseURL;
if (!baseURL) {
throw new Error('playwright.api.config.ts must define use.baseURL');
}
assertDisposableTarget(baseURL);
// API_SKIP_SEED=1 reuses the seed from a previous run against a kept stack
// (fast iteration on a single spec).
const skipSeed = !!process.env.API_SKIP_SEED && existsSync(SEED_FILE);
// Coverage must reflect this run only: the per-process logs are append-only and
// the reporter merges every file in the directory, so stale logs from an earlier
// run would let removed endpoint calls still count as covered.
rmSync(COVERAGE_DIR, { recursive: true, force: true });
mkdirSync(COVERAGE_DIR, { recursive: true });
console.log(`Waiting for ${baseURL}/api/heartbeat ...`);
await waitForHeartbeat(baseURL, HEARTBEAT_TIMEOUT_MS);
if (skipSeed) {
console.log(`Reusing seed state from ${SEED_FILE} (API_SKIP_SEED is set).`);
return;
}
const context = await request.newContext({ baseURL });
try {
const started = Date.now();
const state = await seedEnvironment(new ApiClient(context));
writeFileSync(SEED_FILE, JSON.stringify(state, null, 2));
console.log(
`Seeded ${state.db} environment in ${((Date.now() - started) / 1000).toFixed(1)}s ` +
`(${state.data.expectedPageviews} pageviews on ${state.website.domain})`,
);
} finally {
await context.dispose();
}
}