test(dashboard): add Playwright e2e scaffolding with smoke spec

Milestone A of the frontend test plan. Sets up the test rig — config,
globalSetup that probes the backend and seeds an admin session into
storageState, lightweight fixtures, and a 3-test smoke spec — without
yet covering any user journeys (those land in Milestone B).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-28 07:03:44 +02:00
parent e6fc6e6a0e
commit 74f7b3b631
12 changed files with 531 additions and 47 deletions

View File

@@ -0,0 +1,48 @@
import type { APIRequestContext } from '@playwright/test';
import { adminApi } from './api';
// Resources to delete after a test, in LIFO order. Tests register
// their creations and the registry tears everything down in
// `cleanupRegistered` — typically called from `test.afterEach`.
type Cleanup = (api: APIRequestContext) => Promise<void>;
export class CleanupRegistry {
private items: Cleanup[] = [];
app(slugOrId: string): void {
this.items.push(async (api) => {
await api.delete(`/api/v1/admin/apps/${encodeURIComponent(slugOrId)}?force=true`);
});
}
adminUser(userId: string): void {
this.items.push(async (api) => {
await api.delete(`/api/v1/admin/admins/${userId}`);
});
}
apiKey(keyId: string): void {
this.items.push(async (api) => {
await api.delete(`/api/v1/admin/api-keys/${keyId}`);
});
}
async run(): Promise<void> {
if (this.items.length === 0) return;
const api = await adminApi();
try {
for (const item of this.items.reverse()) {
try {
await item(api);
} catch {
// Best-effort cleanup — a missing resource (already
// deleted by the test) shouldn't fail the suite.
}
}
} finally {
await api.dispose();
this.items = [];
}
}
}