import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { describe, expect, it, vi } from 'vitest'; import { MetricWidget, RankedListWidget, StatusGridWidget, metricStatus, type MetricWidgetProps } from '../../src/MetricWidgets'; const now = Date.parse('2026-08-10T04:00:00Z'); const freshPoint = { timestamp: now, value: 24, freshness: 'fresh' as const, labels: { host: 'tower' } }; function props(overrides: Partial = {}): MetricWidgetProps { return { kind: 'stat', series: [{ key: 'tower', points: [freshPoint] }], freshness: 'fresh', expectedStepSeconds: 15, availability: 'success', metricName: 'host.cpu.utilization', visualization: { unit: 'percent', decimals: 0 }, ...overrides, }; } describe('metricStatus ADR-0008 invariant', () => { it('marks only fresh, available data as ready', () => { expect(metricStatus(props())).toMatchObject({ tone: 'ready' }); }); it.each([ props({ availability: 'idle' }), props({ availability: 'loading' }), props({ availability: 'connecting' }), props({ availability: 'error' }), props({ series: [] }), props({ freshness: 'stale' }), props({ freshness: 'unavailable' }), props({ series: [{ key: 'tower', points: [{ ...freshPoint, freshness: 'delayed' }] }] }), ])('never presents missing, stale or unavailable telemetry as ready', (value) => { expect(metricStatus(value).tone).toBe('unknown'); }); it('renders an explicit unavailable notice without a numeric value', () => { render(); expect(screen.getByText('Bron tijdelijk onbereikbaar.')).toBeVisible(); expect(screen.getByText(/De waarde wordt niet als gezond geïnterpreteerd/)).toBeVisible(); expect(screen.queryByText('24 %')).not.toBeInTheDocument(); }); }); describe('interactive widgets', () => { it('presents metric series labels as human-readable values without raw JSON', () => { const rawKey = '{"interface":"eth0","host":"tower"}'; render(); expect(screen.getByRole('list', { name: 'Legenda' })).toHaveTextContent('tower · eth0'); expect(screen.getByRole('img', { name: /Tijdreeks voor Goedgekeurde meting/ })).toBeVisible(); expect(document.querySelector('.metric-chart-line')).toHaveAttribute('d', 'M 28.000 192.000 L 628.000 192.000'); expect(document.body).not.toHaveTextContent(rawKey); }); it('supports keyboard activation of ranked rows', async () => { const onSelect = vi.fn(); render(); const row = screen.getByRole('button', { name: /Disk 1/ }); row.focus(); await userEvent.keyboard('{Enter}'); expect(onSelect).toHaveBeenCalledWith('disk-1'); }); it('localizes status codes in status-grid fallbacks', () => { render(); expect(screen.getByText('Verstoord')).toBeVisible(); expect(screen.queryByText('degraded')).not.toBeInTheDocument(); }); });