test(ui): add review visual smoke
This commit is contained in:
parent
ca0f64e416
commit
6c562f05d8
97
web/e2e/reviews-visual.spec.ts
Normal file
97
web/e2e/reviews-visual.spec.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { expect, test, type Page, type Route } from "@playwright/test";
|
||||
|
||||
function accessToken() {
|
||||
const payload = Buffer.from(JSON.stringify({ exp: 4102444800 })).toString("base64url");
|
||||
return `test.${payload}.token`;
|
||||
}
|
||||
|
||||
async function seedAuth(page: Page) {
|
||||
await page.addInitScript((token) => {
|
||||
localStorage.setItem("notelett_auth_user", JSON.stringify({ id: "user-1", email: "user@example.com", name: "Review Tester" }));
|
||||
localStorage.setItem("notelett_access_token", token);
|
||||
localStorage.setItem("notelett_refresh_token", "refresh-token");
|
||||
}, accessToken());
|
||||
}
|
||||
|
||||
function json(route: Route, body: unknown, status = 200) {
|
||||
return route.fulfill({
|
||||
status,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
function actionDoc(id: string, afterSummary: string, state: string) {
|
||||
return {
|
||||
id,
|
||||
productId: "notelett",
|
||||
workspaceId: "ws-1",
|
||||
noteId: "note-1",
|
||||
actorId: "agent-1",
|
||||
actorType: "agent",
|
||||
actionType: "update",
|
||||
state,
|
||||
afterSummary,
|
||||
beforeSummary: "Before text",
|
||||
updatedAt: "2026-05-05T00:00:00.000Z",
|
||||
updatedBy: "agent-1",
|
||||
};
|
||||
}
|
||||
|
||||
async function mockReviewApis(page: Page) {
|
||||
await page.route("**/api/auth/refresh", (route) =>
|
||||
json(route, { accessToken: accessToken(), refreshToken: "refresh-token" }),
|
||||
);
|
||||
await page.route("**/api/kill-switch**", (route) =>
|
||||
json(route, { disabled: false, message: null }),
|
||||
);
|
||||
await page.route("**/api/**", (route) => {
|
||||
const url = new URL(route.request().url());
|
||||
const path = url.pathname.replace(/^\/api/, "");
|
||||
|
||||
if (path === "/note-agent-actions/pending") {
|
||||
return json(route, {
|
||||
items: [
|
||||
actionDoc("act-1", "Approve release summary", "proposed"),
|
||||
actionDoc("act-2", "Reject stale task", "proposed"),
|
||||
],
|
||||
total: 2,
|
||||
});
|
||||
}
|
||||
if (path === "/note-agent-actions") {
|
||||
return json(route, {
|
||||
items: [actionDoc("act-3", "Previous review", "approved")],
|
||||
total: 1,
|
||||
});
|
||||
}
|
||||
if (path === "/broadcasts/active" || path === "/surveys/active") {
|
||||
return json(route, { items: [], total: 0 });
|
||||
}
|
||||
|
||||
return json(route, {});
|
||||
});
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await seedAuth(page);
|
||||
await mockReviewApis(page);
|
||||
});
|
||||
|
||||
for (const viewport of [
|
||||
{ name: "desktop", width: 1440, height: 1000 },
|
||||
{ name: "mobile", width: 390, height: 900 },
|
||||
]) {
|
||||
test(`reviews visual smoke - ${viewport.name}`, async ({ page }) => {
|
||||
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
||||
await page.goto("/reviews");
|
||||
await expect(page.getByRole("heading", { name: "Agent review" })).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "Approval queue" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: /Approve release summary/ })).toBeVisible();
|
||||
|
||||
const overflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth);
|
||||
expect(overflow).toBe(false);
|
||||
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot.byteLength).toBeGreaterThan(10_000);
|
||||
});
|
||||
}
|
||||
@ -274,7 +274,7 @@ export default function ReviewsPage() {
|
||||
description="Approval queue, proposal comparison, and audit-oriented review surfaces for agent-mediated edits."
|
||||
actions={<Badge>Operator workflow shell</Badge>}
|
||||
>
|
||||
<section style={{ display: "grid", gridTemplateColumns: "minmax(260px, 320px) minmax(0, 1fr)", gap: "var(--nl-space-4)" }}>
|
||||
<section className="review-workflow-grid">
|
||||
<ReviewWorkflowNav workflows={operatorWorkflows} />
|
||||
|
||||
<Panel>
|
||||
|
||||
@ -136,6 +136,16 @@ button {
|
||||
gap: var(--nl-space-6);
|
||||
}
|
||||
|
||||
.review-workflow-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
|
||||
gap: var(--nl-space-4);
|
||||
}
|
||||
|
||||
.review-workflow-grid > * {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.app-shell {
|
||||
padding-left: 0;
|
||||
@ -151,7 +161,8 @@ button {
|
||||
}
|
||||
|
||||
.note-detail-grid,
|
||||
.workspace-layout-grid {
|
||||
.workspace-layout-grid,
|
||||
.review-workflow-grid {
|
||||
grid-template-columns: minmax(0, 1fr) !important;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user