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:
116
web/tests/e2e/02-dashboard.spec.ts
Normal file
116
web/tests/e2e/02-dashboard.spec.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { loginViaAPI } from "./helpers/auth.js";
|
||||
import { mockDashboard, mockAPIError } from "./helpers/mock-api.js";
|
||||
|
||||
/**
|
||||
* RCA-15: Dashboard
|
||||
*
|
||||
* Covers:
|
||||
* - Stats cards render with correct values
|
||||
* - Device status list
|
||||
* - Quick-action links navigate to correct routes
|
||||
* - Data refresh works
|
||||
* - Error state when API fails
|
||||
*/
|
||||
|
||||
test.describe("Dashboard", () => {
|
||||
test.beforeEach(async ({ page, request }) => {
|
||||
await loginViaAPI(page, request);
|
||||
await mockDashboard(page);
|
||||
});
|
||||
|
||||
test("shows all four stats cards", async ({ page }) => {
|
||||
await page.goto("/dashboard");
|
||||
|
||||
// Connected devices
|
||||
await expect(page.getByText(/connected devices|devices/i).first()).toBeVisible();
|
||||
// Cookie count
|
||||
await expect(page.getByText(/cookie|cookies/i).first()).toBeVisible();
|
||||
// Sync count
|
||||
await expect(page.getByText(/sync/i).first()).toBeVisible();
|
||||
// Uptime
|
||||
await expect(page.getByText(/uptime|running/i).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test("stats cards display values from the API", async ({ page }) => {
|
||||
await page.goto("/dashboard");
|
||||
// Our mock returns: devices total=3, cookies total=142, syncCount=57
|
||||
await expect(page.getByText("3")).toBeVisible();
|
||||
await expect(page.getByText("142")).toBeVisible();
|
||||
await expect(page.getByText("57")).toBeVisible();
|
||||
});
|
||||
|
||||
test("device status list shows online/offline badges", async ({ page }) => {
|
||||
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() },
|
||||
{ id: "d2", name: "Firefox on Windows", platform: "firefox", online: false, lastSeen: "2026-03-15T10:00:00Z" },
|
||||
],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
await page.goto("/dashboard");
|
||||
|
||||
await expect(page.getByText("Chrome on macOS")).toBeVisible();
|
||||
await expect(page.getByText("Firefox on Windows")).toBeVisible();
|
||||
// At least one online/offline indicator
|
||||
const badges = page.getByText(/online|offline/i);
|
||||
await expect(badges.first()).toBeVisible();
|
||||
});
|
||||
|
||||
test("quick action 'View all cookies' navigates to /cookies", async ({ page }) => {
|
||||
await page.goto("/dashboard");
|
||||
await page.getByRole("link", { name: /view all cookie|all cookie|cookie/i }).first().click();
|
||||
await expect(page).toHaveURL(/\/cookies/);
|
||||
});
|
||||
|
||||
test("quick action 'Manage devices' navigates to /devices", async ({ page }) => {
|
||||
await page.goto("/dashboard");
|
||||
await page.getByRole("link", { name: /manage device|devices/i }).first().click();
|
||||
await expect(page).toHaveURL(/\/devices/);
|
||||
});
|
||||
|
||||
test("quick action 'Settings' navigates to /settings", async ({ page }) => {
|
||||
await page.goto("/dashboard");
|
||||
await page.getByRole("link", { name: /setting|settings/i }).first().click();
|
||||
await expect(page).toHaveURL(/\/settings/);
|
||||
});
|
||||
|
||||
test("refresh button re-fetches dashboard data", async ({ page }) => {
|
||||
let callCount = 0;
|
||||
await page.route("**/admin/dashboard", (route) => {
|
||||
callCount++;
|
||||
return route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ devices: {}, cookies: {}, syncCount: callCount, uptimeSeconds: 0 }),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/dashboard");
|
||||
const refreshBtn = page.getByRole("button", { name: /refresh/i });
|
||||
if (await refreshBtn.isVisible()) {
|
||||
const before = callCount;
|
||||
await refreshBtn.click();
|
||||
expect(callCount).toBeGreaterThan(before);
|
||||
}
|
||||
});
|
||||
|
||||
test("shows error message when dashboard API fails", async ({ page }) => {
|
||||
await page.unroute("**/admin/dashboard");
|
||||
await mockAPIError(page, "**/admin/dashboard", 500, "Server error");
|
||||
|
||||
await page.goto("/dashboard");
|
||||
await expect(
|
||||
page
|
||||
.getByRole("alert")
|
||||
.or(page.getByText(/error|failed|unavailable/i))
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user