Files
Lucas WintherandClaude Opus 5.5 974b3eb0df feat(work): CV-first work page, six proud projects, record and courses (spec 009)
- CV header with highlights and keeping-up summary replaces the intro and Now strip
- "Projects I'm proudest of": up to six featured panels; screenshots muted until hover
- "Also made" list; searching it also lists matching courses
- Record section: recognition & volunteering, courses by topic
- /work/learning/: searchable course list (plain script), in the sitemap
- Top bar: Projects, Record, Writing, CV; no Bluesky link in the work footer
- Highlights: only the placement takes the accent (owner, after the preview)

Owner looked at the rendered preview on 2026-11-12 before this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:44:46 +01:00

64 lines
2.8 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { P, I } from './helpers.ts';
test.use({ javaScriptEnabled: false });
test('start page shows both gates', async ({ page }) => {
await page.goto('/');
await expect(page.locator('a[data-enter="work"]')).toBeVisible();
await expect(page.locator('a[data-enter="personal"]')).toBeVisible();
});
test('work page lists every project and hides none', async ({ page }) => {
await page.goto('/work/');
for (const id of [P.byHand2, P.aiMade, P.unknown]) {
await expect(page.locator(`.idx a.row[data-open-sheet="${id}"]`)).toBeVisible();
}
await expect(page.locator('.filtered-note:visible')).toHaveCount(0);
await expect(page.locator('.idx a.row')).toHaveCount(3);
await expect(page.locator('html[data-pref]')).toHaveCount(0);
await expect(page.locator('article.feat:visible')).toHaveCount(6);
});
test('the learning page shows every course without JavaScript', async ({ page }) => {
await page.goto('/work/learning/');
await expect(page.locator('tr[data-course]:visible')).toHaveCount(11);
await expect(page.getByText('No course matches')).toBeHidden();
await expect(page.locator('.ltools')).toBeHidden();
});
test('personal wall shows every card, including AI-marked and unknown', async ({ page }) => {
await page.goto('/off-the-clock/');
const total = await page.locator('[data-wall-card]').count();
expect(total).toBeGreaterThan(8);
// Older years sit in collapsed <details>; nothing may be display:none (that is what the filter does).
const none = await page.$$eval('[data-wall-card]', (cs) => cs.filter((c) => getComputedStyle(c).display === 'none').length);
expect(none).toBe(0);
for (const id of [I.song, I.byHand, I.unknown, I.someAi]) await expect(page.locator(`[data-wall-card][data-id="${id}"]`)).toHaveCount(1);
await expect(page.locator(`[data-wall-card][data-id="${I.someAi}"]`)).toBeVisible();
});
test('project and item pages are complete', async ({ page }) => {
await page.goto(`/work/${P.aiMade}/`);
await expect(page.locator('h1').first()).toBeVisible();
await expect(page.locator('.pref-note')).toBeHidden();
await page.goto(`/off-the-clock/${I.song}/`);
await expect(page.locator(`article.card[data-id="${I.song}"]`)).toBeVisible();
});
test('everything page renders', async ({ page }) => {
const res = await page.goto('/everything/');
expect(res!.status()).toBe(200);
await expect(page.locator('h1').first()).toBeVisible();
});
test('controls that need JavaScript are hidden: preference switch, list tabs and search', async ({ page }) => {
for (const path of ['/', '/work/']) {
await page.goto(path);
await expect(page.locator('html')).not.toHaveClass(/\bjs\b/);
await expect(page.locator('.pref-switch')).toBeHidden();
}
await expect(page.locator('.idx .tools')).toBeHidden();
await expect(page.locator('.idx a.row').first()).toBeVisible();
});