test(web): add Playwright E2E and admin API test suite for RCA-19
Prepares the full QA test infrastructure for the admin frontend before all prerequisite feature tasks (RCA-12–18) are complete. - playwright.config.ts: 6 browser/device projects (Chromium, Firefox, WebKit, mobile Chrome, mobile Safari, tablet) - tests/e2e/01-login.spec.ts: login form, route guards, setup wizard - tests/e2e/02-dashboard.spec.ts: stats cards, device list, quick actions - tests/e2e/03-cookies.spec.ts: cookie list, search, detail panel, delete - tests/e2e/04-devices.spec.ts: device cards, revoke flow, status filter - tests/e2e/05-settings.spec.ts: three-tab layout, save/error toasts - tests/e2e/06-responsive.spec.ts: no horizontal scroll on mobile/tablet - tests/api/admin-api.spec.ts: REST API contract tests for all /admin/* endpoints - helpers/auth.ts: loginViaUI + loginViaAPI helpers - helpers/mock-api.ts: route intercept fixtures for all pages Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
153
web/tests/e2e/helpers/mock-api.ts
Normal file
153
web/tests/e2e/helpers/mock-api.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { type Page } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Intercept /admin/dashboard and return a canned response so UI tests
|
||||
* don't depend on a running relay server with real data.
|
||||
*/
|
||||
export async function mockDashboard(page: Page): Promise<void> {
|
||||
await page.route("**/admin/dashboard", (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
devices: { total: 3, online: 2, offline: 1 },
|
||||
cookies: { total: 142, domains: 8 },
|
||||
syncCount: 57,
|
||||
uptimeSeconds: 86400,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept /admin/cookies and return a paginated list.
|
||||
*/
|
||||
export async function mockCookies(page: Page): Promise<void> {
|
||||
await page.route("**/admin/cookies*", (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
cookies: [
|
||||
{
|
||||
id: "c1",
|
||||
domain: "example.com",
|
||||
name: "session",
|
||||
value: "abc123",
|
||||
path: "/",
|
||||
expires: "2027-01-01T00:00:00Z",
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
},
|
||||
{
|
||||
id: "c2",
|
||||
domain: "example.com",
|
||||
name: "pref",
|
||||
value: "dark",
|
||||
path: "/",
|
||||
expires: "2027-06-01T00:00:00Z",
|
||||
secure: false,
|
||||
httpOnly: false,
|
||||
},
|
||||
{
|
||||
id: "c3",
|
||||
domain: "other.io",
|
||||
name: "token",
|
||||
value: "xyz",
|
||||
path: "/",
|
||||
expires: null,
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
},
|
||||
],
|
||||
total: 3,
|
||||
page: 1,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept /admin/devices and return device list.
|
||||
*/
|
||||
export async function mockDevices(page: Page): Promise<void> {
|
||||
await page.route("**/admin/devices*", (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
devices: [
|
||||
{
|
||||
id: "d1",
|
||||
name: "Chrome on macOS",
|
||||
platform: "chrome",
|
||||
online: true,
|
||||
lastSeen: new Date().toISOString(),
|
||||
registeredAt: "2026-01-01T00:00:00Z",
|
||||
ipAddress: "192.168.1.10",
|
||||
extensionVersion: "2.0.0",
|
||||
},
|
||||
{
|
||||
id: "d2",
|
||||
name: "Firefox on Windows",
|
||||
platform: "firefox",
|
||||
online: false,
|
||||
lastSeen: "2026-03-15T10:00:00Z",
|
||||
registeredAt: "2026-02-01T00:00:00Z",
|
||||
ipAddress: null,
|
||||
extensionVersion: "2.0.0",
|
||||
},
|
||||
],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept /admin/settings and return settings object.
|
||||
*/
|
||||
export async function mockSettings(page: Page): Promise<void> {
|
||||
await page.route("**/admin/settings*", (route) => {
|
||||
if (route.request().method() === "GET") {
|
||||
return route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
sync: {
|
||||
autoSync: true,
|
||||
frequency: "realtime",
|
||||
domainWhitelist: [],
|
||||
domainBlacklist: [],
|
||||
},
|
||||
security: {
|
||||
sessionTimeoutMinutes: 60,
|
||||
requirePairingPin: false,
|
||||
},
|
||||
appearance: {
|
||||
theme: "system",
|
||||
language: "zh",
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
return route.fulfill({ status: 200, contentType: "application/json", body: "{}" });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a 500 error on the given path — used for error-handling tests.
|
||||
*/
|
||||
export async function mockAPIError(
|
||||
page: Page,
|
||||
urlPattern: string,
|
||||
status = 500,
|
||||
message = "Internal Server Error",
|
||||
): Promise<void> {
|
||||
await page.route(urlPattern, (route) =>
|
||||
route.fulfill({
|
||||
status,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ error: message }),
|
||||
}),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user