From 7d381e2d2e47a25f2ee1416a25038f7260dc6411 Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Sun, 16 Aug 2026 13:48:53 -0500 Subject: [PATCH] test: add Playwright + axe specs for home, archive, single --- tests/archive.spec.js | 25 +++++++++++++++++++++++++ tests/home.spec.js | 28 ++++++++++++++++++++++++++++ tests/single.spec.js | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 tests/archive.spec.js create mode 100644 tests/home.spec.js create mode 100644 tests/single.spec.js diff --git a/tests/archive.spec.js b/tests/archive.spec.js new file mode 100644 index 0000000..8b5529a --- /dev/null +++ b/tests/archive.spec.js @@ -0,0 +1,25 @@ +const { test, expect } = require('@playwright/test'); +const AxeBuilder = require('@axe-core/playwright').default; + +test.use({ viewport: { width: 1920, height: 1080 } }); + +test.describe('archive-projects', () => { + test('project archive renders grid or empty + zero axe violations', async ({ page }, testInfo) => { + await page.goto('/projects/'); + await page.screenshot({ path: 'test-results/archive.png', fullPage: true }); + + await expect(page.locator('.archive-projects__head h1')).toBeVisible(); + const grid = page.locator('.archive-projects__grid'); + const empty = page.locator('.archive-projects__empty'); + await expect(grid.or(empty)).toBeVisible(); + + const results = await new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa']) + .analyze(); + await testInfo.attach('axe', { + body: JSON.stringify(results, null, 2), + contentType: 'application/json', + }); + expect(results.violations).toEqual([]); + }); +}); diff --git a/tests/home.spec.js b/tests/home.spec.js new file mode 100644 index 0000000..41e7979 --- /dev/null +++ b/tests/home.spec.js @@ -0,0 +1,28 @@ +const { test, expect } = require('@playwright/test'); +const AxeBuilder = require('@axe-core/playwright').default; + +test.use({ viewport: { width: 1920, height: 1080 } }); + +test.describe('home', () => { + test('home renders Carbon Blue hero + recent projects + zero axe violations', async ({ page }, testInfo) => { + await page.goto('/'); + await page.screenshot({ path: 'test-results/home.png', fullPage: true }); + + // Hero region must be present. + await expect(page.locator('.home-hero')).toBeVisible(); + + // Either the grid OR the empty state must be present. + const grid = page.locator('.home-recent__grid'); + const empty = page.locator('.home-recent__empty'); + await expect(grid.or(empty)).toBeVisible(); + + const results = await new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa']) + .analyze(); + await testInfo.attach('axe', { + body: JSON.stringify(results, null, 2), + contentType: 'application/json', + }); + expect(results.violations).toEqual([]); + }); +}); diff --git a/tests/single.spec.js b/tests/single.spec.js new file mode 100644 index 0000000..83000d8 --- /dev/null +++ b/tests/single.spec.js @@ -0,0 +1,36 @@ +const { test, expect } = require('@playwright/test'); +const AxeBuilder = require('@axe-core/playwright').default; + +test.use({ viewport: { width: 1920, height: 1080 } }); + +test.describe('single-project', () => { + test('project detail renders hero, sidebar, no axe violations', async ({ page }, testInfo) => { + // Pick the first project; skip the test gracefully if none exist. + const response = await page.goto('/projects/'); + expect(response && response.status()).toBeLessThan(400); + + const firstLink = page.locator('.archive-projects__grid a.project-card__title-link').first(); + if (await firstLink.count() === 0) { + test.skip(true, 'No projects to test detail page'); + return; + } + + await Promise.all([ + page.waitForLoadState('networkidle'), + firstLink.click(), + ]); + await page.screenshot({ path: 'test-results/single.png', fullPage: true }); + + await expect(page.locator('.single-project__title')).toBeVisible(); + await expect(page.locator('.single-project__sidebar')).toBeVisible(); + + const results = await new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa']) + .analyze(); + await testInfo.attach('axe', { + body: JSON.stringify(results, null, 2), + contentType: 'application/json', + }); + expect(results.violations).toEqual([]); + }); +});