import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; const sourceRoot = path.resolve("src"); function sourceFiles(directory) { return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { const target = path.join(directory, entry.name); if (entry.isDirectory()) return sourceFiles(target); return /\.(tsx|ts)$/.test(entry.name) ? [target] : []; }); } const sources = sourceFiles(sourceRoot).map((file) => ({ file, value: fs.readFileSync(file, "utf8"), })); const nonSemanticClicks = sources.flatMap(({ file, value }) => [...value.matchAll(/<(div|span|li|tr)\b[^>]*\bonClick\s*=/g)].map( (match) => `${path.relative(sourceRoot, file)}:${match.index}`, ), ); assert.deepEqual( nonSemanticClicks, [], `Use a semantic button/link instead of clickable layout elements: ${nonSemanticClicks.join(", ")}`, ); const preview = fs.readFileSync( path.join(sourceRoot, "features/campaigns/components/MessagePreviewOverlay.tsx"), "utf8", ); const styles = fs.readFileSync( path.join(sourceRoot, "styles/campaign-workspace.css"), "utf8", ); const jsonView = fs.readFileSync( path.join(sourceRoot, "features/campaigns/CampaignJsonView.tsx"), "utf8", ); const auditView = fs.readFileSync( path.join(sourceRoot, "features/campaigns/CampaignAuditPage.tsx"), "utf8", ); const attachmentPreview = fs.readFileSync( path.join(sourceRoot, "features/campaigns/review/AttachmentLinkingPreview.tsx"), "utf8", ); assert.ok( jsonView.includes('DismissibleAlert tone="warning" dismissible={false}'), "The full Campaign JSON projection must retain an explicit privacy warning", ); assert.ok( auditView.includes("ActionBlockerHint"), "Unavailable Campaign audit projection must retain an actionable shared blocker", ); assert.ok( attachmentPreview.includes(''), "Attachment-preview validation failures must use the compact shared alert", ); assert.ok( styles.includes("@media (prefers-reduced-motion: reduce)"), "Campaign must retain an explicit reduced-motion presentation contract", ); assert.ok( styles.includes("@media (max-width:"), "Campaign must retain responsive narrow-viewport layouts", ); assert.ok( styles.includes(":focus-visible"), "Campaign-specific interactive controls must retain visible keyboard focus", ); for (const handler of ["onFirst", "onPrevious", "onNext", "onLast"]) { const buttonLine = preview .split("\n") .find((line) => line.includes(`