Files
Portfolio/e2e/systems-map.e2e.ts
Antonio Ledebuhr bb9912daef Keep the default-open terminal inside the viewport and the map labels inside their shapes.
The dock prompt and placeholder now meet contrast on the raised surface, and service nodes use the same rect geometry as the cases.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 00:43:56 +02:00

103 lines
4.0 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { SIGNATURE_COPY } from '../src/app/core/content/signature-copy';
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('services', 'de'));
for (const width of [320, 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('services', '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('data-id') ?? 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('services', '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 path = href.split('#')[0];
const response = await request.get(path);
expect(response.status(), href).toBe(200);
}
});
test('plain activation opens the gist dialog without navigating', async ({ page }) => {
const copy = SIGNATURE_COPY.de.systemsMap;
await page.goto(pagePath('services', 'de'));
await page.setViewportSize({ width: 390, height: 844 });
const first = page.locator('.systems-map-cards a').first();
const href = await first.getAttribute('href');
expect(href).toContain('#infrastructure-network');
await first.click();
const dialog = page.getByRole('dialog');
await expect(dialog).toBeVisible();
await expect(page).toHaveURL(new RegExp(`${pagePath('services', 'de')}$`));
await expect(dialog.getByRole('link')).toHaveAttribute('href', href ?? '');
await expect(dialog.getByRole('button', { name: copy.closeLabel })).toBeVisible();
const dialogBox = await dialog.boundingBox();
expect(dialogBox, 'dialog readable at 390px').toBeTruthy();
expect(dialogBox!.width).toBeGreaterThan(200);
await page.keyboard.press('Escape');
await expect(dialog).toHaveCount(0);
await expect(first).toBeFocused();
});
test('a modified click follows the node href', async ({ page }) => {
await page.goto(pagePath('services', 'de'));
const first = page.locator('.systems-map-cards a').first();
await first.click({ modifiers: ['Control'] });
await expect(page.getByRole('dialog')).toHaveCount(0);
});
});