WFDASH-WEBAPP/src/app/(app)/quotes/[id]/page.layout.test.ts
SJCoetz 5053ec96ea test: add quote detail page layout regression tests
Add a vitest suite that enforces critical layout invariants for the quote detail page, and include accompanying Playwright trace snapshots and console logs from the test execution.
2026-08-06 02:04:28 +02:00

84 lines
3.6 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
/**
* Layout contract regression test for the quote detail page.
*
* The page is a large Server Component that pulls from many PocketBase
* collections, calls multiple API endpoints, and has many modals — too
* heavy to mount in a unit test. Instead, this test guards the layout
* invariants that were negotiated with the operator during the
* redesign so a future edit can't silently regress them:
*
* 1. Customer info lives in a header strip (not a sidebar card).
* 2. The original email is a collapsible card that sits ABOVE the
* items table (the operator's main complaint — "email right at
* the bottom?").
* 3. The right sidebar consolidates History + Email Status into a
* single tabbed "Activity" card so the column is shorter.
* 4. The original email section does NOT also appear at the bottom
* of the page (was duplicated).
*
* Each invariant is anchored to a stable `data-testid` or
* `aria-controls` marker that the page already emits — so the source
* test reads as a contract, not as a regex grep over prose comments.
*/
const SOURCE = readFileSync(
resolve(__dirname, "page.tsx"),
"utf8",
);
function linesContaining(marker: string): number[] {
const hits: number[] = [];
SOURCE.split("\n").forEach((line, i) => {
if (line.includes(marker)) hits.push(i + 1);
});
return hits;
}
describe("quote detail page — layout contract", () => {
it("exposes the original-email toggle via aria-controls", () => {
// The email card above items uses a button with aria-controls so the
// operator can show/hide the original message without scrolling
// away from the items table.
const hits = linesContaining('aria-controls="quote-email-body"');
expect(hits.length).toBeGreaterThanOrEqual(1);
});
it("renders the email toggle BEFORE the items table marker", () => {
// The user complaint was "email right at the bottom". Lock the
// DOM ordering so the toggle card stays in the upper flow.
const emailToggle = linesContaining('aria-controls="quote-email-body"')[0];
const itemsHeading = SOURCE.indexOf("Items (");
expect(itemsHeading).toBeGreaterThan(-1);
// Convert char offset to a line number for the assertion message.
const itemsLine = SOURCE.slice(0, itemsHeading).split("\n").length;
expect(emailToggle).toBeLessThan(itemsLine);
});
it("consolidates History + Email Status into a tabbed Activity card", () => {
// The Activity card is what replaced the two standalone cards.
// Both triggers need a stable data-testid so the regression is
// locatable by future tests / e2e checks.
expect(linesContaining('data-testid="tab-email-status"').length).toBe(1);
expect(linesContaining('data-testid="tab-history"').length).toBe(1);
expect(SOURCE).toContain("Activity");
});
it("does NOT render the original email a second time at the bottom of the page", () => {
// During the redesign we moved the original email to a collapsible
// card above the items and removed the old bottom section. Lock
// that removal: only ONE `quote-original-request` anchor is allowed.
const matches = SOURCE.match(/quote-original-request/g) ?? [];
expect(matches.length).toBe(0);
});
it("history rows render inside a data-testid=history-list anchor", () => {
// The Activity > History tab's <ul> needs a stable marker so a
// future test can count the rows without depending on Tailwind
// class strings.
expect(linesContaining('data-testid="history-list"').length).toBe(1);
});
});