89 lines
3.3 KiB
JavaScript
89 lines
3.3 KiB
JavaScript
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/(localized)/[locale]/projects/layout.tsx',
|
|
'src/app/(localized)/[locale]/projects/[projectId]/page.tsx',
|
|
];
|
|
|
|
test('localized project routes use runtime content and metadata', 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.doesNotMatch(pages, /generateStaticParams/);
|
|
assert.doesNotMatch(pages, /dynamicParams\s*=\s*false/);
|
|
assert.match(pages, /await params/);
|
|
assert.match(pages, /generateMetadata/);
|
|
});
|
|
|
|
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:/);
|
|
assert.match(share, /react-icons\/fa6/);
|
|
assert.match(share, /FaTelegram/);
|
|
assert.match(share, /FaWhatsapp/);
|
|
assert.match(share, /FaLinkedinIn/);
|
|
});
|