import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; import type { FindingRecord } from '../../src/domain/finding'; import { dashboardExplicitIoeCount, dashboardExplicitIoeRecordIds, dashboardPingCastleRiskCategories, dashboardRiskTools, dashboardScanSourceLabel } from '../../src/features/dashboard/DashboardPanel'; const component = readFileSync(new URL('../../src/features/dashboard/DashboardPanel.tsx', import.meta.url), 'utf8'); const styles = readFileSync(new URL('../../src/styles/datahub.css', import.meta.url), 'utf8'); describe('dashboard reference layout', () => { it('uses the reference metric strip for compliance, primary statuses and the compact summary', () => { expect(component).toContain('className="dashboard-status-rack"'); expect(component).toContain('label="Compliance Score"'); expect(component).toContain('Not Applicable'); expect(component).toContain('Error'); expect(component).toContain('className="dashboard-ioe-summary" type="button"'); expect(component).toContain('setPathFindingIds(explicitIoeRecordIds)'); expect(styles).toMatch(/\.dashboard-status-rack\s*\{[^}]*repeat\(3,/s); expect(styles).toContain('.dashboard-status-ring-track { stroke: var(--donut-border); }'); expect(styles).toContain('button.dashboard-status-metric:hover { border-color: currentColor; }'); }); it('uses paired segmented distributions and a compact lower dashboard row', () => { expect(component).toContain('heading="Severity distribution"'); expect(component).toContain('heading="Scan source distribution"'); expect(component).toContain('className="dashboard-lower-grid"'); expect(styles).toMatch(/\.dashboard-distribution-grid\s*\{[^}]*grid-template-columns:\s*repeat\(2,/s); expect(styles).toContain('.dashboard-segmented-bar'); }); it('matches the reference structure for directory coverage and risk profile', () => { expect(component).toContain('className="dashboard-coverage-summary"'); expect(component).toContain('typed observations'); expect(component).toContain('className="dashboard-block dashboard-risk-profile"'); expect(component).toContain('className="dashboard-marked-summary"'); expect(component).toContain('className="dashboard-risk-tool"'); expect(component).toContain(''); expect(component).toContain('className="dashboard-risk-donut"'); expect(component).toContain('categories.slice(0, 4)'); expect(component).toContain('matchesDynamicFacets(record, [sourceFacet]'); expect(component).not.toContain('dashboard-review-block'); expect(styles).toMatch(/\.dashboard-facts\s*\{[^}]*grid-template-columns:\s*repeat\(4,/s); expect(styles).toMatch(/\.dashboard-risk-body\s*\{[^}]*grid-template-columns:/s); }); it('removes only a trailing scan timestamp from source labels', () => { expect(dashboardScanSourceLabel('PurpleKnight · 2026-08-22T08:15:30.000Z')).toBe('PurpleKnight'); expect(dashboardScanSourceLabel('CIS-CAT Pro · 2026-08-22')).toBe('CIS-CAT Pro'); expect(dashboardScanSourceLabel('Scanner · Production')).toBe('Scanner · Production'); }); it('groups scan runs into one selectable risk profile per tool', () => { expect(dashboardRiskTools([ { value: 'pingcastle-a', label: 'PingCastle · 2026-08-22T08:15:30.000Z', count: 20 }, { value: 'pingcastle-b', label: 'PingCastle · 2026-08-23T08:15:30.000Z', count: 24 }, { value: 'purpleknight-a', label: 'PurpleKnight · 2026-08-23T09:00:00.000Z', count: 30 } ])).toEqual([ { id: 'pingcastle', label: 'PingCastle', sourceValues: ['pingcastle-a', 'pingcastle-b'], count: 44 }, { id: 'purpleknight', label: 'PurpleKnight', sourceValues: ['purpleknight-a'], count: 30 } ]); }); it('keeps the complete PingCastle risk catalog including zero scores', () => { expect(dashboardPingCastleRiskCategories([ { property: 'risk.anomalies', normalizedValue: 20, originalValue: '20' }, { property: 'risk.stale_objects', normalizedValue: 10, originalValue: '10' }, { property: 'risk.privileged_accounts', normalizedValue: 7, originalValue: '7' }, { property: 'risk.trusts', normalizedValue: 0, originalValue: '0' } ], [])).toEqual([ { value: 'anomalies', label: 'Anomalies', count: 20 }, { value: 'stale objects', label: 'Stale Objects', count: 10 }, { value: 'privileged accounts', label: 'Privileged Accounts', count: 7 }, { value: 'trusts', label: 'Trusts', count: 0 } ]); }); it('counts only explicit IOE-found source results for the top summary', () => { const record = (id: string, value: string): FindingRecord => ({ id, index: 0, title: id, fields: [{ index: 0, label: 'Result', value }], filled: [], meta: [] }); expect(dashboardExplicitIoeCount([ record('ioe', 'IOE Found'), record('ioes', 'IOEs found'), record('deviation', 'Failed'), record('pass', 'Passed') ])).toBe(2); expect(dashboardExplicitIoeRecordIds([ record('ioe', 'IOE Found'), record('ioes', 'IOEs found'), record('deviation', 'Failed') ])).toEqual(['ioe', 'ioes']); }); });