import { renderToStaticMarkup } from 'react-dom/server'; import { describe, expect, it } from 'vitest'; import { makeSheet } from '../../src/importers/sheet-factory'; import { genericProfile } from '../../src/profiles/generic/generic-profile'; import { securityDomainCatalog } from '../../src/features/dashboard/security-domains'; import { summarizeSecurityDomains, toggleSecurityDomainSelection } from '../../src/features/sidebar/SidebarSecurityDomains'; import { deriveDynamicFacets } from '../../src/features/search-filter/dynamic-facets'; import { compactSeveritySelection, DynamicFilterControls } from '../../src/features/search-filter/DynamicFilterControls'; // Synthetic structured Raw Data: one check per domain (user decision 2026-10-04, six domains). const rows = [ ['Title', 'Severity', 'Result', 'Source: Scan', 'Category', 'Source Rule ID'], ['Inactive account', 'Critical', 'Failed', 'Purple Knight for Active Directory · synthetic', 'Account Security', 'PK-1'], ['Privileged administrator', 'High', 'Failed', 'Purple Knight for Active Directory · synthetic', 'AD Delegation', 'PK-2'], ['Certificate expired', 'Medium', 'Failed', 'Purple Knight for Active Directory · synthetic', 'AD Infrastructure Security', 'PK-3'], ['GPO logon script', 'Low', 'Failed', 'Purple Knight for Active Directory · synthetic', 'Group Policy Security', 'PK-4'], ['Exchange setting', 'High', 'Failed', 'Maester · synthetic', 'Maester/Exchange', 'MT.9001'], ['Audit unavailable', 'Low', 'Failed', 'PingCastle · synthetic', 'Anomalies', 'A-AuditDC'], ['Account passed', 'Medium', 'Passed', 'Purple Knight for Active Directory · synthetic', 'Account Security', 'PK-5'] ]; function options(statusFilter = 'deviation', facetFilters = {}) { return { sheet: makeSheet('Synthetic findings', rows, '', 'synthetic.csv'), dataset: null, activeProfile: genericProfile, reviews: {}, statusFilter, facetFilters, search: '', actionOnly: false }; } describe('sidebar security domains', () => { it('uses the exact Client Info catalog order without an Unclassified entry', () => { const summary = summarizeSecurityDomains(options()); expect(summary?.domains.map(domain => domain.label)).toEqual(securityDomainCatalog.map(domain => domain.label)); expect(summary?.domains.map(domain => domain.count)).toEqual([1, 1, 1, 1, 1, 1]); }); it('keeps all six catalog domains selectable at zero hits and follows active filters', () => { const summary = summarizeSecurityDomains(options('passed')); expect(summary?.domains.map(domain => domain.id)).toEqual(securityDomainCatalog.map(domain => domain.id)); expect(summary?.domains.find(domain => domain.id === 'identity')?.count).toBe(1); expect(summary?.domains.filter(domain => domain.id !== 'identity').every(domain => domain.count === 0)).toBe(true); }); it('uses single selection and clears the active domain on a second click', () => { expect(toggleSecurityDomainSelection([], 'identity')).toEqual(['identity']); expect(toggleSecurityDomainSelection(['identity'], 'hardening')).toEqual(['hardening']); expect(toggleSecurityDomainSelection(['hardening'], 'hardening')).toEqual([]); }); it('does not render a rack without an active sheet', () => { expect(summarizeSecurityDomains({ ...options(), sheet: undefined })).toBeUndefined(); }); }); describe('Work filter controls retained beside the sidebar domain filter', () => { it('renders a compact multi-severity label in the filter control', () => { const base = options(); const severityFacet = deriveDynamicFacets(base.sheet, null, genericProfile, {}) .find(facet => facet.severityScale); if (!severityFacet) throw new Error('Synthetic severity facet missing'); const html = renderToStaticMarkup( undefined} onFilter={() => undefined}/>); expect(compactSeveritySelection(['info', 'critical', 'medium'])).toBe('Crit-Med-Inf'); expect(html).toContain('Crit-High-Med-Low-Inf'); expect(html).toContain('value="__severity_multi__" selected=""'); }); it('keeps Source: Scan as the final right-edge filter control', () => { const scanSheet = makeSheet('Synthetic findings', [ ['Title', 'Severity', 'Source: Scan'], ['One', 'High', 'Scanner A'], ['Two', 'Low', 'Scanner B'] ], '', 'synthetic.csv'); const facets = deriveDynamicFacets(scanSheet, null, genericProfile, {}); const html = renderToStaticMarkup( undefined} onFilter={() => undefined}/>); expect(facets.at(-1)?.label).toBe('Source: Scan'); expect(html).toContain('dynamic-facet-select is-source-scan'); }); });