Files
ITWorx-Pulse-Public/apps/web/tests/e2e/management-workspace.spec.ts
T
2026-09-30 01:14:48 +00:00

107 lines
6.1 KiB
TypeScript

import AxeBuilder from '@axe-core/playwright';
import { expect, test } from '@playwright/test';
import path from 'node:path';
const systemStatus = {
version: '1.3.0',
release: { version: '1.3.0', commit: 'abc1234', builtAt: '2026-08-21T12:00:00Z', migrationVersion: '0024' },
generatedAt: new Date().toISOString(),
overallState: 'healthy',
components: [],
backup: { state: 'healthy', reason: 'backup_verified', ageSeconds: 30 * 60 * 60, verifiedAt: '2026-08-20T06:00:00Z' },
sourceLag: [{ sourceId: 'unraid', state: 'healthy', reason: 'fresh', ageSeconds: 5 }],
};
const onboarding = {
state: { completed: true, step: 'completed', dashboardChoice: 'default', rulesChoice: 'default', dashboardId: 'overview', rulesReady: true },
capabilities: [
{ id: 'auth', state: 'ready', detail: 'Aanmelding geconfigureerd.' },
{ id: 'database', state: 'ready', detail: 'Database beschikbaar.' },
{ id: 'prometheus', state: 'ready', detail: 'Meetgegevens beschikbaar.' },
{ id: 'unraid', state: 'ready', detail: 'Unraid-bron beschikbaar.' },
],
resume: false,
};
test('beheerhub, afgeronde onboarding en backupouderdom blijven taakgericht en waarheidsgetrouw', async ({ page }, testInfo) => {
test.skip(testInfo.project.name === 'wallboard-chromium', 'Beheerflows gebruiken de desktop-, tablet- en mobiele shell.');
await page.route('**/api/v1/**', async (route) => {
const pathname = new URL(route.request().url()).pathname;
if (pathname === '/api/v1/system/status') {
if (route.request().method() === 'POST') {
await route.fulfill({ status: 403, contentType: 'application/problem+json', body: '{}' });
return;
}
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ ...systemStatus, generatedAt: new Date().toISOString() }) });
return;
}
if (pathname === '/api/v1/onboarding') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify(onboarding) });
return;
}
if (pathname === '/api/v1/host') {
await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ hardware: { status: { state: 'healthy' }, capabilities: [], temperatures: [], fans: [], gpus: [] } }) });
return;
}
await route.fulfill({ status: 404, contentType: 'application/problem+json', body: '{}' });
});
await page.goto('/settings');
await expect(page.getByRole('heading', { name: 'Pulse configureren' })).toBeVisible();
const hub = page.getByRole('region', { name: 'Beheerfuncties' });
await expect(hub.getByRole('link')).toHaveCount(8);
await expect(hub.getByRole('link', { name: /Systeemstatus en backup/ })).toHaveAttribute('href', '/status');
await expect(hub.getByRole('link', { name: /Eerste configuratie/ })).toHaveAttribute('href', '/onboarding');
await expect(hub.getByRole('link', { name: /Alertregels/ })).toHaveAttribute('href', '/alerts?section=rules');
await expect(hub.getByRole('link', { name: /Stiltes en onderhoud/ })).toHaveAttribute('href', '/alerts?section=controls');
await expect(hub.getByText('Wijzigen: beheerder')).toBeVisible();
await expect(hub.getByText('Wijzigen: operator')).toBeVisible();
const capabilities = page.getByRole('region', { name: 'Capabilitystatus' });
await expect(capabilities).toContainText('Unraid');
await expect(capabilities).toContainText('HardwaretelemetrieBeschikbaar');
await expect(capabilities).toContainText('UPSNiet geconfigureerd');
await expect(capabilities.getByRole('link', { name: 'Open systeemstatus en back-up' })).toHaveAttribute('href', '/status');
if (process.env.PULSE_CAPTURE_VISUALS && (testInfo.project.name === 'desktop-chromium' || testInfo.project.name === 'mobile-chromium')) {
await page.screenshot({ path: path.resolve('../../artifacts/evidence/M14-03', `settings-${testInfo.project.name}.png`), fullPage: true });
}
await hub.getByRole('link', { name: /Systeemstatus en backup/ }).click();
const backup = page.getByRole('article').filter({ has: page.getByText('Backupstatus', { exact: true }) });
await expect(page.getByRole('heading', { name: 'Pulse-systeemstatus' })).toBeVisible();
await expect(backup.locator('.status-badge')).toContainText('Aandacht');
await expect(backup.getByText(/backup is verlopen/i)).toBeVisible();
await expect(backup.getByText(/1 dag geleden/)).toBeVisible();
await expect(backup.getByText(/ouder dan 24 uur/)).toBeVisible();
await backup.getByRole('button', { name: 'Maak geverifieerde backup' }).click();
await expect(backup.getByRole('alert')).toContainText('Alleen beheerders');
await page.goto('/settings');
await page.getByRole('region', { name: 'Beheerfuncties' }).locator('a[href="/onboarding"]').click();
await expect(page.getByRole('heading', { name: 'Pulse is geconfigureerd' })).toBeVisible();
await expect(page.getByRole('radio')).toHaveCount(0);
const reconfigure = page.getByRole('button', { name: 'Herconfiguratie openen' });
if (testInfo.project.name === 'mobile-chromium') {
await reconfigure.scrollIntoViewIfNeeded();
expect(await reconfigure.evaluate((button) => {
const box = button.getBoundingClientRect();
const target = document.elementFromPoint(box.left + box.width / 2, box.top + box.height / 2);
return target === button || button.contains(target);
})).toBe(true);
await reconfigure.focus();
await page.keyboard.press('Enter');
} else {
await reconfigure.click();
}
await expect(page.getByRole('radio')).toHaveCount(4);
await page.getByRole('button', { name: 'Annuleren' }).click();
await expect(page.getByRole('radio')).toHaveCount(0);
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1)).toBe(true);
const axe = await new AxeBuilder({ page }).analyze();
expect(axe.violations.filter((item) => item.impact === 'serious' || item.impact === 'critical')).toEqual([]);
if (testInfo.project.name === 'mobile-chromium') {
const linkHeights = await page.locator('.settings-hub-card a').evaluateAll((links) => links.map((link) => link.getBoundingClientRect().height));
expect(linkHeights.every((height) => height >= 44)).toBe(true);
}
});