import { expect, test } from '@playwright/test'; const dashboard = { id: 'wallboard-dashboard', slug: 'operations', name: 'Operaties', description: 'Kritieke infrastructuur en recente gebeurtenissen.', scope: 'system', revision: 1, currentVersion: 1 }; const widgets = [ ['system', 'Serverstatus', 0, 0, 6, 4, 'inventory', { entityType: 'server' }], ['cpu', 'CPU en belasting', 6, 0, 10, 6, 'text', {}], ['storage', 'Array en pools', 0, 6, 12, 7, 'text', {}], ['apps', 'Applicaties', 12, 6, 12, 7, 'text', {}], ['events', 'Recente gebeurtenissen', 0, 13, 24, 6, 'events', {}], ].map(([id, title, x, y, w, h, sourceType, scope]) => ({ id, title, type: id === 'events' ? 'event-timeline' : 'stat', data: { sourceType, scope, limit: 12 }, behavior: { liveIntervalSeconds: 30 }, layouts: { wallboard: { x, y, w, h, visible: true }, desktop: { x: 0, y: 0, w: 6, h: 4, visible: true } }, })); test('wallboard rotates bounded 1080p slides with truthful transport and data status', async ({ page }, testInfo) => { test.skip(testInfo.project.name !== 'wallboard-chromium', 'Requires the 1920x1080 wallboard viewport.'); let dashboardReads = 0; await page.route('**/api/v1/**', async (route) => { const path = new URL(route.request().url()).pathname; if (path === '/api/v1/dashboards') { dashboardReads += 1; // React development mode performs an initial StrictMode re-read. Fail the // first scheduled refresh, not either of the initial bootstrap reads. if (dashboardReads === 3) { await route.fulfill({ status: 503, contentType: 'application/json', body: JSON.stringify({ error: 'temporary_unavailable' }) }); return; } } const body = path === '/api/v1/dashboards' ? { items: [dashboard] } : path === '/api/v1/dashboards/wallboard-dashboard' ? { dashboard, version: { document: { widgets, variables: [] } } } : path === '/api/v1/system/status' ? { version: '1', generatedAt: new Date().toISOString(), overallState: 'degraded', components: [{ id: 'storage', state: 'degraded', reason: 'capacity_critical' }], backup: { state: 'disabled', reason: 'not_configured' }, sourceLag: [{ sourceId: 'unraid', state: 'healthy', reason: 'fresh' }] } : path === '/api/v1/services' ? { services: [{ id: 'up', state: 'up' }, { id: 'down', state: 'down' }] } : path === '/api/v1/incidents' ? { items: [{ id: 'incident-1' }] } : path === '/api/v1/events' ? { items: [{ id: 'event-1', type: 'service.down', severity: 'critical', summary: 'Service niet beschikbaar', occurredAt: new Date().toISOString() }] } : {}; await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(body) }); }); const startedAt = Date.now(); await page.goto('/wallboard?interval=15&refresh=10'); await expect(page.getByRole('heading', { level: 1, name: 'Operationeel wallboard' })).toBeVisible(); expect(Date.now() - startedAt).toBeLessThan(3_000); await expect(page.getByText('Slide 1 / 2')).toBeVisible(); await expect(page.locator('.wallboard-connection').filter({ hasText: 'Transport' })).toContainText('Verbonden'); await expect(page.locator('.wallboard-connection').filter({ hasText: 'Data' })).toContainText('Bruikbaar'); await expect(page.locator('.wallboard-priority')).toContainText('OpslagVerstoord'); await expect(page.locator('.wallboard-priority')).toContainText('Services1 problemen'); await expect(page.locator('.wallboard-priority')).toContainText('Incidenten1 open'); await expect(page.getByRole('button', { name: /Bewerken|Exporteren/ })).toHaveCount(0); expect(await page.evaluate(() => ({ horizontal: document.documentElement.scrollWidth - innerWidth, vertical: document.documentElement.scrollHeight - innerHeight }))).toEqual({ horizontal: 0, vertical: 0 }); if (process.env.PULSE_CAPTURE_VISUALS) await page.screenshot({ path: `../../artifacts/evidence/M14-04/wallboard-slide-1-${testInfo.project.name}.png` }); await expect(page.getByText('Slide 2 / 2')).toBeVisible({ timeout: 18_000 }); await expect(page.getByRole('heading', { level: 3, name: 'Recente gebeurtenissen' })).toBeVisible(); await expect(page.locator('.wallboard-connection').filter({ hasText: 'Transport' })).toContainText('Bron niet beschikbaar', { timeout: 12_000 }); await expect(page.locator('.wallboard-connection').filter({ hasText: 'Data' })).toContainText('Bruikbaar'); await expect(page.locator('.wallboard-connection').filter({ hasText: 'Transport' })).toContainText('Verbonden', { timeout: 12_000 }); await expect(page.getByText('Slide 2 / 2')).toBeVisible(); expect(await page.evaluate(() => ({ horizontal: document.documentElement.scrollWidth - innerWidth, vertical: document.documentElement.scrollHeight - innerHeight }))).toEqual({ horizontal: 0, vertical: 0 }); if (process.env.PULSE_CAPTURE_VISUALS) await page.screenshot({ path: `../../artifacts/evidence/M14-04/wallboard-slide-2-${testInfo.project.name}.png` }); });