integration: add Playwright, axe and Lighthouse CI gates
Lock SEO, accessibility and crawlability with headless browser checks so regressions fail before a merge instead of after publication. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
66
e2e/systems-map.e2e.ts
Normal file
66
e2e/systems-map.e2e.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { pagePath } from './helpers';
|
||||
|
||||
test.describe('Systems Map', () => {
|
||||
test('keeps the card list visible and shows the SVG from 1024px', async ({ page }) => {
|
||||
await page.goto(pagePath('home', 'de'));
|
||||
|
||||
for (const width of [390, 768, 1024, 1440]) {
|
||||
await page.setViewportSize({ width, height: 900 });
|
||||
const list = page.locator('.systems-map-list');
|
||||
await expect(list).toBeVisible();
|
||||
await expect(list.locator('.systems-map-cards li')).toHaveCount(8);
|
||||
|
||||
const figure = page.locator('.systems-map-figure');
|
||||
if (width < 1024) {
|
||||
await expect(figure).toBeHidden();
|
||||
} else {
|
||||
await expect(figure).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('keeps SVG labels inside their shapes at 1024 and 1440', async ({ page }) => {
|
||||
await page.goto(pagePath('home', 'de'));
|
||||
|
||||
for (const width of [1024, 1440]) {
|
||||
await page.setViewportSize({ width, height: 900 });
|
||||
const overflow = await page.evaluate(() => {
|
||||
const nodes = Array.from(document.querySelectorAll<SVGGElement>('.systems-map-node'));
|
||||
return nodes.flatMap((node) => {
|
||||
const shape = node.querySelector('circle, rect');
|
||||
const text = node.querySelector('text');
|
||||
if (!shape || !text) {
|
||||
return [`${node.getAttribute('aria-label') ?? 'node'} missing shape or text`];
|
||||
}
|
||||
|
||||
const shapeBox = (shape as SVGGraphicsElement).getBBox();
|
||||
const textBox = (text as SVGGraphicsElement).getBBox();
|
||||
const fits =
|
||||
textBox.x >= shapeBox.x - 0.5 &&
|
||||
textBox.y >= shapeBox.y - 0.5 &&
|
||||
textBox.x + textBox.width <= shapeBox.x + shapeBox.width + 0.5 &&
|
||||
textBox.y + textBox.height <= shapeBox.y + shapeBox.height + 0.5;
|
||||
|
||||
return fits ? [] : [node.getAttribute('aria-label') ?? 'unnamed node'];
|
||||
});
|
||||
});
|
||||
|
||||
expect(overflow, `labels overflow at ${width}px`).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
test('every SVG node points at a real route', async ({ page, request }) => {
|
||||
await page.goto(pagePath('home', 'de'));
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
const hrefs = await page
|
||||
.locator('.systems-map-node')
|
||||
.evaluateAll((nodes) => nodes.map((node) => node.getAttribute('href') ?? ''));
|
||||
|
||||
expect(hrefs.length).toBeGreaterThan(0);
|
||||
for (const href of hrefs) {
|
||||
const response = await request.get(href);
|
||||
expect(response.status(), href).toBe(200);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user