import assert from 'node:assert/strict'; import { access, readFile } from 'node:fs/promises'; import test from 'node:test'; const routeFiles = [ 'src/app/(en)/projects/layout.tsx', 'src/app/(en)/projects/[projectId]/page.tsx', 'src/app/(fa)/fa/projects/layout.tsx', 'src/app/(fa)/fa/projects/[projectId]/page.tsx', ]; test('localized project routes are statically generated and reject unknown slugs', async () => { await Promise.all(routeFiles.map((file) => access(file))); const pages = ( await Promise.all( routeFiles .filter((file) => file.endsWith('page.tsx')) .map((file) => readFile(file, 'utf8')), ) ).join('\n'); assert.match(pages, /generateStaticParams/); assert.match(pages, /dynamicParams\s*=\s*false/); assert.match(pages, /await params/); assert.match(pages, /generateMetadata/); }); test('every project exposes an evidence-based localized case study', async () => { const source = await readFile('src/data/projects.ts', 'utf8'); assert.doesNotMatch(source, /\blink\??:/); assert.doesNotMatch(source, /\blink:/); assert.ok((source.match(/\bcaseStudy:/g) ?? []).length >= 14); assert.equal((source.match(/\bgallery:\s*\[/g) ?? []).length, 7); assert.match(source, /export const getProject\s*=/); assert.match(source, /export const getAdjacentProjects\s*=/); assert.match(source, /export const getProjectPath\s*=/); assert.match(source, /screenshots\/qlockify\/qlockify-landing\.png/); assert.match(source, /screenshots\/guilan-ace\/guilan-ace-landing-dark\.png/); assert.match(source, /screenshots\/subsource\/subtitle-detail-light\.png/); }); test('project cards separate case study navigation from live links', async () => { const source = await readFile('src/components/ProjectsSection.tsx', 'utf8'); assert.match(source, /import Link from ['"]next\/link['"]/); assert.match(source, /scroll=\{false\}/); assert.match(source, /target="_blank"/); assert.match(source, /rel="noopener noreferrer"/); assert.match(source, /project\.liveUrl/); assert.match(source, /caseStudyInput/); assert.match(source, /closest\('a, button/); }); test('the case study drawer has accessible and pointer-safe dismissal paths', async () => { const [component, styles] = await Promise.all([ Promise.all([ 'src/components/CaseStudyDrawer.tsx', 'src/components/CaseStudyGallery.tsx', 'src/components/CaseStudyShare.tsx', 'src/components/useDrawerDrag.ts', ].map((file) => readFile(file, 'utf8'))).then((files) => files.join('\n')), readFile('src/components/CaseStudyDrawer.module.css', 'utf8'), ]); assert.match(component, /showModal\(\)/); assert.match(component, /onCancel=/); assert.match(component, /setPointerCapture/); assert.match(component, /navigator\.clipboard/); assert.match(component, /navigator\.share/); assert.match(component, /aria-live="polite"/); assert.match(component, /data-case-study-initial-focus/); assert.doesNotMatch(component, /data-case-study-heading/); assert.match(component, /unoptimized/); assert.match(component, /quality=\{60\}/); assert.match(component, /aria-haspopup="dialog"/); assert.match(styles, /::backdrop/); assert.match(styles, /env\(safe-area-inset-bottom/); assert.match(styles, /prefers-reduced-motion:\s*reduce/); assert.doesNotMatch(styles, /transition:\s*all/); }); test('the full-screen gallery and share fallback expose complete interaction paths', async () => { const [gallery, share] = await Promise.all([ readFile('src/components/CaseStudyGallery.tsx', 'utf8'), readFile('src/components/CaseStudyShare.tsx', 'utf8'), ]); assert.match(gallery, /ArrowLeft/); assert.match(gallery, /ArrowRight/); assert.match(gallery, /onPointerDown/); assert.match(gallery, /onPointerUp/); assert.match(gallery, /scrollIntoView/); assert.match(share, /t\.me\/share\/url/); assert.match(share, /wa\.me/); assert.match(share, /linkedin\.com\/sharing\/share-offsite/); assert.match(share, /mailto:/); });