Files
ITWorx Pulse release export bd774932d5
Public source validation / validate (push) Failing after 3m8s
Publish ITWorx Pulse source
2026-09-03 02:09:19 +02:00

130 lines
7.3 KiB
TypeScript

import AxeBuilder from '@axe-core/playwright';
import { expect, test, type Page } from '@playwright/test';
import path from 'node:path';
const generatedAt = new Date().toISOString();
async function mockAPI(page: Page): Promise<void> {
await page.route('**/api/v1/**', async (route) => {
const path = new URL(route.request().url()).pathname;
if (path === '/api/v1/system/status') {
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify({
version: '1', generatedAt, overallState: 'degraded',
components: [{ id: 'database', state: 'healthy', reason: 'ok' }, { id: 'prometheus', state: 'unknown', reason: 'source_stale' }],
backup: { state: 'disabled', reason: 'not_configured' },
sourceLag: [{ sourceId: 'reverse-proxy', state: 'unknown', reason: 'source_unavailable', ageSeconds: 180 }],
}),
});
return;
}
if (path === '/api/v1/dashboards') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ items: [] }) });
return;
}
if (path === '/api/v1/host') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({
identity: { name: 'mobile-fixture' },
cpu: { totalPercent: 42, perCore: Array.from({ length: 16 }, (_, index) => index + 1) },
memory: { utilizationPercent: 61 }, source: { state: 'healthy', freshness: 'fresh' },
}) });
return;
}
if (path === '/api/v1/containers') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ source: { state: 'healthy', freshness: 'fresh' }, containers: [{ id: 'proxy', state: 'running', health: 'healthy' }], total: 1 }) });
return;
}
if (path === '/api/v1/pools') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ source: { state: 'healthy', freshness: 'fresh' }, pools: [{ id: 'cache', name: 'Cache', state: 'healthy', utilizationPercent: 63 }], total: 1 }) });
return;
}
if (path === '/api/v1/services') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ capabilityState: 'available', configurationState: 'configured', services: [{ id: 'proxy', name: 'Proxy', state: 'up' }], total: 1 }) });
return;
}
if (path === '/api/v1/incidents') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ items: [{ id: 'incident-1', title: 'Bronvertraging', severity: 'warning', startedAt: generatedAt }] }) });
return;
}
await route.fulfill({ status: 404, contentType: 'application/problem+json', body: JSON.stringify({ code: 'NOT_FOUND' }) });
});
}
async function expectNoCriticalA11yViolations(page: Page): Promise<void> {
const results = await new AxeBuilder({ page }).analyze();
const severe = results.violations.filter((violation) => violation.impact === 'critical' || violation.impact === 'serious');
expect(severe, severe.map((violation) => `${violation.id}: ${violation.help}`).join('\n')).toEqual([]);
}
test.beforeEach(async ({ page }) => {
await mockAPI(page);
});
test('overview exposes degraded/unknown state and passes axe', async ({ page }, testInfo) => {
test.skip(testInfo.project.name === 'wallboard-chromium', 'Wallboard has a dedicated route test.');
await page.goto('/');
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
await expect(page.getByText(/Onbekend|Verminderd/).first()).toBeVisible();
if (testInfo.project.name === 'mobile-chromium') {
await expect(page.locator('.desktop-navigation')).toBeHidden();
await expect(page.locator('.mobile-navigation')).toBeVisible();
await expect(page.locator('.mobile-navigation')).toHaveCSS('position', 'fixed');
await expect(page.locator('.mobile-primary-list .nav-link')).toHaveCount(4);
const mobileTargets = await page.locator('.mobile-primary-list .nav-link').evaluateAll((items) => items.map((item) => item.getBoundingClientRect().height));
expect(mobileTargets.every((height) => height >= 44)).toBe(true);
await expect(page.locator('.signal-path-stage')).toHaveCount(6);
await expect(page.locator('.signal-path-inspector')).toBeVisible();
const incidentBox = await page.locator('.incident-queue').boundingBox();
const signalBox = await page.locator('.signal-path-panel').boundingBox();
expect(incidentBox?.y).toBeLessThan(signalBox?.y ?? 0);
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
} else {
await expect(page.locator('.desktop-navigation')).toBeVisible();
const rail = await page.locator('.sidebar').boundingBox();
const commandHeader = await page.locator('.context-bar').boundingBox();
expect(rail?.width).toBeLessThanOrEqual(72);
expect(commandHeader?.height).toBe(56);
await expect(page.locator('.overview-kpi')).toHaveCount(4);
await expect(page.locator('.source-health-strip')).toHaveAttribute('tabindex', '0');
await expect(page.locator('.nav-group')).toHaveCount(6);
await expect(page.locator('.nav-group').first()).toHaveAttribute('open', '');
await expect(page.locator('.nav-group').filter({ hasText: 'Infrastructuur' })).not.toHaveAttribute('open', '');
}
await page.keyboard.press('Tab');
await expect(page.locator(':focus')).toBeVisible();
await expectNoCriticalA11yViolations(page);
if (process.env.PULSE_E2E_REAL_BASE_URL || process.env.PULSE_CAPTURE_VISUALS) {
const evidenceDirectory = process.env.PULSE_CAPTURE_VISUALS ? 'M14-02' : 'M11-10';
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur());
await page.screenshot({ path: path.resolve('../../artifacts/evidence', evidenceDirectory, `overview-${testInfo.project.name}.png`), fullPage: true });
}
});
test('grouped navigation reaches infrastructure in two actions', async ({ page }, testInfo) => {
test.skip(testInfo.project.name === 'wallboard-chromium', 'Wallboard heeft geen productnavigatie.');
await page.goto('/');
if (testInfo.project.name === 'mobile-chromium') {
await page.getByText('Meer', { exact: true }).click();
await page.getByRole('link', { name: /Disks/ }).click();
} else {
await page.locator('.nav-group').filter({ hasText: 'Infrastructuur' }).locator('summary').click();
await page.getByRole('link', { name: /Disks/ }).click();
}
await expect(page).toHaveURL(/\/disks$/);
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1)).toBe(true);
});
test('wallboard remains read-only, bounded and accessible', async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== 'wallboard-chromium', 'Wallboard is verified at 1920x1080.');
await page.goto('/wallboard');
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
await expect(page.getByText(/Geen dashboards|Wallboard/).first()).toBeVisible();
await expect(page.locator('.sidebar')).toHaveCount(0);
await expect(page.getByRole('button', { name: /Bewerken|Exporteren/ })).toHaveCount(0);
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1)).toBe(true);
expect(await page.evaluate(() => document.documentElement.scrollHeight <= window.innerHeight + 1)).toBe(true);
await expectNoCriticalA11yViolations(page);
});