Files
tech-log-frontend/scripts/lib/tech-log-serving-contract.ts
T
DongHyeonkaandClaude Opus 5 cf45bcc7dc test: give the Asset Library a visual golden, and a URL that resolves
`/studio/assets` was the one route in the TechLog route contract missing
from `TECH_LOG_CANONICAL_ROUTES` and `TECH_LOG_STUDIO_STATE_PATHS`, so it
had no golden, no responsive-overflow check, and no Axe scan. That gap was
not hypothetical: the responsive overflow just fixed in the Asset search row
shipped in two places and was only caught in the Picker, which has a golden;
the Library's identical copy would have gone unseen.

Verifying the render before capturing anything turned up a second defect.
`studioSpaPathPatterns` in the production serving contract never listed
`/studio/assets`, so a hard navigation or reload of that URL was answered
with the in-shell Studio 404 -- the screen was reachable only by client-side
navigation from another Studio page. A golden taken then would have frozen a
404. With the pattern added, the route serves 200 text/html like its
siblings, and the page renders correctly at both breakpoints:
scrollWidth == clientWidth == viewport at 360 and 1440, `main` and the `h1`
visible, the search label/input/button all inside the 360px viewport, and no
critical or serious Axe violations in chromium.

Three new goldens, no existing golden regenerated:
tech-log-studio-assets-{360,1440} from the canonical route list and
tech-log-studio-asset-library-1440 from the Studio state list, matching how
every other Studio route appears in both. `pnpm test:visual` is 133 passed,
up from 130.

The demo profile seeds no assets, so the golden captures the empty state --
which still covers the header, heading, search row and status region, the
surface the overflow regression lived on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 14:12:09 +09:00

83 lines
2.4 KiB
TypeScript

export type TechLogServingContract = Readonly<{
schemaVersion: 1;
publicSpaPaths: readonly string[];
studioPathPrefix: "/studio";
studioSpaPathPatterns: readonly string[];
notFound: Readonly<{
status: 404;
contentType: "text/plain;charset=UTF-8";
body: "Not Found";
}>;
}>;
type ServingContractInput = Readonly<{
publicRecords: readonly Readonly<{
path: string;
topicSlug: string;
}>[];
projects: readonly Readonly<{ slug: string }>[];
releases: readonly Readonly<{ path: string }>[];
}>;
const staticPublicPaths = Object.freeze([
"/",
"/explore",
"/explore/cases",
"/explore/questions",
"/explore/references",
"/profile",
"/projects",
"/releases",
"/search",
]);
const studioSpaPathPatterns = Object.freeze([
"^/studio$",
// The Asset Library is a first-class Studio route (TECH_LOG_STUDIO_ASSETS in
// the route contract) but was never listed here, so a hard navigation or a
// reload of /studio/assets was served the in-shell Studio 404 -- the screen
// was only reachable by client-side navigation from another Studio page.
"^/studio/assets$",
"^/studio/documents$",
"^/studio/documents/new$",
"^/studio/documents/[^/]+/(edit|validation|preview|publish)$",
"^/studio/publications$",
"^/studio/publications/[^/]+/preview$",
]);
function asciiCompare(left: string, right: string): number {
return left < right ? -1 : left > right ? 1 : 0;
}
export function createTechLogServingContract({
publicRecords,
projects,
releases,
}: ServingContractInput): TechLogServingContract {
const publicSpaPaths = new Set(staticPublicPaths);
for (const record of publicRecords) {
publicSpaPaths.add(record.path);
publicSpaPaths.add(`/topics/${record.topicSlug}`);
}
for (const project of projects) {
const projectPath = `/projects/${project.slug}`;
publicSpaPaths.add(projectPath);
publicSpaPaths.add(`${projectPath}/activity`);
publicSpaPaths.add(`${projectPath}/decisions`);
publicSpaPaths.add(`${projectPath}/records`);
}
for (const release of releases) publicSpaPaths.add(release.path);
return Object.freeze({
schemaVersion: 1,
publicSpaPaths: Object.freeze([...publicSpaPaths].sort(asciiCompare)),
studioPathPrefix: "/studio",
studioSpaPathPatterns,
notFound: Object.freeze({
status: 404,
contentType: "text/plain;charset=UTF-8",
body: "Not Found",
}),
});
}