Public source validation / validate (push) Failing after 3m8s
92 lines
6.3 KiB
TypeScript
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 });
|
|
});
|