Files
ITWorx-Pulse-Public/apps/web/tests/e2e/dashboard-editor-polish.spec.ts
T
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

92 lines
6.3 KiB
TypeScript

import AxeBuilder from '@axe-core/playwright';
import { expect, test, type Page } from '@playwright/test';
import path from 'node:path';
const dashboard = { id: 'polish-dashboard', slug: 'operations', name: 'Netwerkoperaties', description: 'Actuele netwerkbelasting en operationele wijzigingen.', scope: 'system', revision: 4, currentVersion: 7 };
const widgets = [
{
id: 'network', title: 'Netwerkbelasting', type: 'timeseries',
data: { sourceType: 'semantic-metric', metric: 'host.network.receive', aggregation: 'avg' },
visualization: { unit: 'bytesPerSecond', decimals: 0, legend: true },
behavior: { locked: false, hidden: false, liveIntervalSeconds: 30 },
layouts: { desktop: { x: 0, y: 0, w: 9, h: 5, visible: true } },
},
{
id: 'events', title: 'Recente wijzigingen', type: 'event-timeline',
data: { sourceType: 'events', limit: 12 }, behavior: { locked: false, hidden: false, liveIntervalSeconds: 30 },
layouts: { desktop: { x: 9, y: 0, w: 9, h: 5, visible: true } },
},
];
async function mockDashboard(page: Page): Promise<void> {
await page.route('**/api/v1/**', async (route) => {
const requestPath = new URL(route.request().url()).pathname;
const body = requestPath === '/api/v1/dashboards/polish-dashboard'
? { dashboard, version: { document: { schemaVersion: 2, widgets, variables: [], settings: { defaultTimeRange: '1h' } } } }
: requestPath === '/api/v1/metrics/query-range'
? { status: 'success', data: { result: [{ metric: { __name__: 'host_network_receive', host: 'tower', interface: 'eth0' }, values: [[1786420800, '1200'], [1786420815, '1500']] }] }, provenance: { source: 'prometheus', metric: 'host.network.receive', catalogVersion: '1', cacheKey: 'test' }, sourceObservedAt: '2026-08-11T04:01:00Z', receivedAt: '2026-08-11T04:01:01Z', freshness: 'fresh', cacheHit: false }
: requestPath === '/api/v1/events'
? { items: [{ id: 'event-1', type: 'container.restart', severity: 'warning', summary: 'container.restart', occurredAt: '2026-08-11T04:00:00Z' }] }
: requestPath === '/api/v1/system/status'
? { version: '1', generatedAt: '2026-08-11T04:01:00Z', overallState: 'healthy', components: [{ id: 'database', state: 'healthy', reason: 'database_ready' }], backup: { state: 'healthy', reason: 'backup_verified' }, sourceLag: [] }
: {};
await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(body) });
});
}
async function expectNoSeriousAxeViolations(page: Page): Promise<void> {
const results = await new AxeBuilder({ page }).analyze();
const violations = results.violations.filter((item) => item.impact === 'critical' || item.impact === 'serious');
expect(violations, violations.map((item) => `${item.id}: ${item.help}`).join('\n')).toEqual([]);
}
test('dashboard and editor use human labels, safe modes and accessible keyboard controls', async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== 'desktop-chromium', 'The editor acceptance proof uses the desktop canvas.');
await mockDashboard(page);
await page.goto('/dashboards/polish-dashboard');
await expect(page.getByRole('heading', { level: 1, name: 'Netwerkoperaties' })).toBeVisible();
await expect(page.getByRole('heading', { level: 2, name: 'Dashboardwidgets' })).toBeAttached();
await expect(page.getByRole('heading', { level: 3, name: 'Netwerkbelasting' })).toBeVisible();
await expect(page.getByRole('list', { name: 'Legenda' })).toContainText('tower · eth0');
await expect(page.locator('.metric-chart-line')).toHaveAttribute('d', 'M 28.000 192.000 L 628.000 12.000');
await expect(page.getByText('Container herstart', { exact: false })).toBeVisible();
await expect(page.getByText('container.restart', { exact: true })).toHaveCount(0);
await expect(page.locator('body')).not.toContainText('{"__name__"');
await expectNoSeriousAxeViolations(page);
if (process.env.PULSE_CAPTURE_VISUALS) await page.screenshot({ path: path.resolve('../../artifacts/evidence/M12-07/dashboard-human-labels.png'), fullPage: true });
await page.getByRole('button', { name: 'Bewerken' }).click();
await expect(page.getByRole('heading', { level: 1, name: 'Dashboard aanpassen' })).toBeVisible();
await expect(page.getByRole('heading', { level: 2, name: 'Dashboardindeling' })).toBeAttached();
const advanced = page.getByText('Dashboardvariabelen, sjablonen en gegevensoverdracht', { exact: true });
await expect(advanced).toBeVisible();
await expect(page.getByRole('heading', { name: 'Import, export en templates' })).toBeHidden();
await expect(page.locator('.editor-widget-actions').first()).toContainText('Omhoog');
await expect(page.locator('.editor-widget-actions').first()).toContainText('Omlaag');
await expect(page.locator('.editor-widget-actions').first()).toContainText('Breedte');
await expect(page.getByText('Weergavemodus')).toHaveCount(0);
const editorWidgets = page.locator('.editor-widget');
await expect(editorWidgets.locator('h3')).toHaveText(['Netwerkbelasting', 'Recente wijzigingen']);
const firstWidget = await editorWidgets.first().boundingBox();
expect(firstWidget).not.toBeNull();
await page.mouse.move(firstWidget!.x + 30, firstWidget!.y + 30);
await page.mouse.down();
await page.mouse.move(firstWidget!.x + 30, firstWidget!.y + 75, { steps: 4 });
await page.mouse.up();
await expect(editorWidgets.locator('h3')).toHaveText(['Recente wijzigingen', 'Netwerkbelasting']);
const resize = page.getByRole('slider', { name: 'Breedte aanpassen: Netwerkbelasting' });
await expect(resize).toHaveAttribute('aria-valuenow', '9');
await resize.focus();
await page.keyboard.press('ArrowRight');
await expect(resize).toHaveAttribute('aria-valuenow', '10');
if (process.env.PULSE_CAPTURE_VISUALS) await page.screenshot({ path: path.resolve('../../artifacts/evidence/M12-07/editor-keyboard-and-disclosure.png'), fullPage: true });
await advanced.click();
await expect(page.getByRole('heading', { level: 2, name: 'Import, export en templates' })).toBeVisible();
await expectNoSeriousAxeViolations(page);
expect(await page.evaluate(() => document.documentElement.scrollWidth <= document.documentElement.clientWidth)).toBe(true);
if (process.env.PULSE_CAPTURE_VISUALS) await page.screenshot({ path: path.resolve('../../artifacts/evidence/M12-07/editor-advanced-open.png'), fullPage: true });
});