033c9ec315
- 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>
63 lines
2.3 KiB
TypeScript
63 lines
2.3 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
||
import { alternates, buildJsonLd, absUrl, ogImagePath } from '@lib/seo';
|
||
import { PAGE_IDS } from '@i18n/slugMap';
|
||
|
||
const ORIGIN = 'https://cesnimda.co.uk/';
|
||
|
||
describe('SEO builders (SEO_SPEC §2–4)', () => {
|
||
it('alternates include en, nb and x-default for every page', () => {
|
||
for (const id of PAGE_IDS) {
|
||
const alts = alternates(id, ORIGIN);
|
||
const langs = alts.map((a) => a.hreflang);
|
||
expect(langs).toContain('en');
|
||
expect(langs).toContain('nb');
|
||
expect(langs).toContain('x-default');
|
||
for (const a of alts) expect(a.href.startsWith('https://')).toBe(true);
|
||
}
|
||
});
|
||
|
||
it('x-default points at the English URL', () => {
|
||
const alts = alternates('projects', ORIGIN);
|
||
const xd = alts.find((a) => a.hreflang === 'x-default');
|
||
const en = alts.find((a) => a.hreflang === 'en');
|
||
expect(xd?.href).toBe(en?.href);
|
||
});
|
||
|
||
it('home JSON-LD is a valid Person graph', () => {
|
||
const graph = buildJsonLd('home', 'en', ORIGIN);
|
||
const person = graph.find((g) => (g as { '@type': string })['@type'] === 'Person') as Record<
|
||
string,
|
||
unknown
|
||
>;
|
||
expect(person).toBeTruthy();
|
||
expect(person.name).toBe('Connor Babbington');
|
||
expect(person.jobTitle).toBe('Systems Developer');
|
||
expect(() => JSON.stringify(graph)).not.toThrow();
|
||
});
|
||
|
||
it('Norwegian home uses the localised job title', () => {
|
||
const graph = buildJsonLd('home', 'no', ORIGIN);
|
||
const person = graph.find((g) => (g as { '@type': string })['@type'] === 'Person') as Record<
|
||
string,
|
||
unknown
|
||
>;
|
||
expect(person.jobTitle).toBe('Systemutvikler');
|
||
});
|
||
|
||
it('case-study JSON-LD carries the project name and author', () => {
|
||
const graph = buildJsonLd('jobtrack', 'en', ORIGIN, {
|
||
projectName: 'JobTrack',
|
||
projectType: 'SoftwareApplication',
|
||
});
|
||
const work = graph[0] as Record<string, unknown>;
|
||
expect(work['@type']).toBe('SoftwareApplication');
|
||
expect(work.name).toBe('JobTrack');
|
||
expect((work.author as Record<string, unknown>)['@type']).toBe('Person');
|
||
});
|
||
|
||
it('helpers build absolute URLs and OG paths', () => {
|
||
expect(absUrl('/projects/', ORIGIN)).toBe('https://cesnimda.co.uk/projects/');
|
||
expect(ogImagePath('home', 'no')).toBe('/og/home-no.png');
|
||
});
|
||
});
|