- 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>
64 lines
2.8 KiB
TypeScript
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();
|
|
});
|