test: vitest unit + playwright e2e suites; fix a11y, contrast, no-JS reveal
- vitest: dictionary parity, slug-map bijection, content-schema validation, JSON-LD (23) - playwright: both locales, language-switch mapping (incl. no-JS), CV downloads, form happy-path + honeypot, axe a11y on 5 templates x 2 themes (23) - fix: raise ink-faint + light accent to meet WCAG AA 4.5:1 (axe-verified) - fix: gate reveal animations behind html.js so content is visible without JS (progressive enhancement - was hidden at opacity 0); scan reduced-motion path - fix: validate contact form before the anti-bot time-trap (no false success) - chore: ESLint node globals, typed diagram props, resvg fontFiles, prettier pass Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
getProfile,
|
||||
getSkills,
|
||||
getExperience,
|
||||
getProjectCards,
|
||||
getProject,
|
||||
getProjectIds,
|
||||
getMeta,
|
||||
} from '@lib/content';
|
||||
import { PAGE_IDS } from '@i18n/slugMap';
|
||||
import { LOCALES } from '@i18n/locales';
|
||||
|
||||
// Importing @lib/content runs every zod schema parse at module load; a schema
|
||||
// violation would throw here and fail the suite (DATA_MODEL §8).
|
||||
|
||||
describe('content loads and validates', () => {
|
||||
it('profile resolves in both locales', () => {
|
||||
for (const l of LOCALES) {
|
||||
const p = getProfile(l);
|
||||
expect(p.name).toBe('Connor Babbington');
|
||||
expect(p.chips.length).toBe(4);
|
||||
expect(p.aboutParagraphs.length).toBe(3);
|
||||
}
|
||||
});
|
||||
|
||||
it('exactly three projects, correctly ordered and typed', () => {
|
||||
const cards = getProjectCards('en');
|
||||
expect(cards.map((c) => c.id)).toEqual(['jobtrack', 'inboxintel', 'homelab']);
|
||||
expect(cards.find((c) => c.id === 'inboxintel')?.status).toBe('in-development');
|
||||
});
|
||||
|
||||
it('every case-study project has decisions and a security section (promise to P2)', () => {
|
||||
for (const id of getProjectIds()) {
|
||||
const p = getProject(id, 'en');
|
||||
if (p.template !== 'case-study') continue;
|
||||
const kinds = p.sections.map((s) => s.kind);
|
||||
expect(kinds, id).toContain('security');
|
||||
const decisions = p.sections.find((s) => s.kind === 'decisions');
|
||||
expect(decisions?.decisions?.length ?? 0, id).toBeGreaterThanOrEqual(3);
|
||||
}
|
||||
});
|
||||
|
||||
it('skills expose three groups', () => {
|
||||
expect(getSkills('no').map((g) => g.id)).toEqual([
|
||||
'development',
|
||||
'devops-infrastructure',
|
||||
'practices',
|
||||
]);
|
||||
});
|
||||
|
||||
it('experience features the council role with progression', () => {
|
||||
const w = getExperience('en').find((i) => i.id === 'warwickshire');
|
||||
expect(w?.emphasis).toBe('featured');
|
||||
expect(w?.progression?.length).toBe(2);
|
||||
});
|
||||
|
||||
it('page meta exists for every page in both locales, within length limits', () => {
|
||||
for (const id of PAGE_IDS) {
|
||||
for (const l of LOCALES) {
|
||||
const m = getMeta(id, l);
|
||||
expect(m.title.length, `${id}.${l} title`).toBeLessThanOrEqual(70);
|
||||
expect(m.description.length, `${id}.${l} desc`).toBeLessThanOrEqual(160);
|
||||
expect(m.description.length).toBeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user