Files
cesnimda 033c9ec315 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>
2026-07-04 06:14:18 +02:00

41 lines
1.7 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('smoke — both locales render (TECH_SPEC §9.3)', () => {
test('English home', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/Systems Developer/);
await expect(page.locator('h1')).toHaveText('Connor Babbington');
await expect(page.locator('html')).toHaveAttribute('lang', 'en-GB');
// decision facts present in the first view (chip)
await expect(page.getByText('Tønsberg, Norway', { exact: true }).first()).toBeVisible();
});
test('Norwegian home', async ({ page }) => {
await page.goto('/no/');
await expect(page).toHaveTitle(/Systemutvikler/);
await expect(page.locator('html')).toHaveAttribute('lang', 'nb-NO');
await expect(page.getByText('Gyldig oppholdstillatelse')).toBeVisible();
});
test('hreflang alternates emitted', async ({ page }) => {
await page.goto('/');
await expect(page.locator('link[hreflang="en"]')).toHaveCount(1);
await expect(page.locator('link[hreflang="nb"]')).toHaveCount(1);
await expect(page.locator('link[hreflang="x-default"]')).toHaveCount(1);
});
test('case study shows architecture and decisions', async ({ page }) => {
await page.goto('/projects/jobtrack/');
await expect(page.locator('h1')).toHaveText('JobTrack');
await expect(page.locator('#architecture svg[role="img"]')).toBeVisible();
await expect(page.locator('#decisions')).toBeVisible();
});
test('skip link is the first focusable element', async ({ page }) => {
await page.goto('/');
await page.keyboard.press('Tab');
const focused = page.locator(':focus');
await expect(focused).toHaveText(/Skip to content/);
});
});