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('.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); }); });