import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; import type { FindingRecord } from '../../src/domain/finding'; import { compactTileBadges, tileBadges, TileCard } from '../../src/components/TileCard/TileCard'; import { policyFindingCategories } from '../../src/profiles/group-policy/categories'; const record: FindingRecord = { id: 'tile-1', index: 0, title: 'Enterprise applications using SAML for SSO', fields: [ { index: 0, label: 'Score', value: '86' }, { index: 1, label: 'Severity', value: 'Low' }, { index: 2, label: 'Weight', value: '2' }, { index: 3, label: 'Category', value: 'Entra ID' }, { index: 4, label: 'Result', value: 'IOE Found' } ], filled: [], meta: [] }; describe('tileBadges', () => { it('uses the same GPO categories as the detail view and also shows scope', () => { const gpo: FindingRecord = { ...record, title: 'system access · minimumpasswordlength', fields: [ { index: 0, label: 'Category', value: 'GPOs' }, { index: 1, label: 'Scope', value: 'computer' }, { index: 2, label: 'Baseline', value: 'Windows Server 2022 Security Baseline' }, { index: 3, label: 'Status', value: 'Fail' } ] }; const badges = tileBadges(gpo, 'group-policy'); expect(badges.filter(b => b.label === 'GPO Category').map(b => b.text)).toEqual(policyFindingCategories(gpo, 'group-policy')); expect(badges.map(b => b.text)).toEqual(['Fail', 'GPOs', 'Windows', 'Security', 'System Access', 'Password Policy', 'Scope: computer']); const html = renderToStaticMarkup(createElement(TileCard, { record: gpo, title: gpo.title, bookmarked: false, toolId: 'group-policy', onOpen() {}, onBookmark() {} })); for (const category of ['Windows', 'Security', 'System Access', 'Password Policy']) expect(html).toContain(`data-badge-role="category">${category}`); expect(html).toContain('data-badge-role="scope">Scope: computer'); expect(html).not.toContain('tile-badge-overflow'); }); it('recognizes GPOs in a mixed-tool tile grid', () => { const gpo: FindingRecord = { ...record, title: 'privilege rights · senetworklogonright', fields: [ { index: 0, label: 'Source: Scan', value: 'GPOs · Synthetic' }, { index: 1, label: 'Scope', value: 'user' } ] }; expect(tileBadges(gpo, 'generic').map(b => b.text)).toEqual(['Security', 'Privilege Rights', 'Logon Rights', 'Scope: user']); }); it('deduplicates categories but keeps an explicitly named scope', () => { const gpo: FindingRecord = { ...record, fields: [ { index: 0, label: 'Category', value: 'Office' }, { index: 1, label: 'Baseline', value: 'Office 2016 Security Baseline' }, { index: 2, label: 'Scope', value: 'computer' }, { index: 3, label: 'Scope', value: 'computer' } ] }; expect(tileBadges(gpo, 'group-policy').map(b => b.text)).toEqual(['Office', 'Scope: computer']); }); it('does not add GPO badges to other tools or invent missing scope', () => { const other: FindingRecord = { ...record, fields: [ { index: 0, label: 'Scope', value: 'computer' }, { index: 1, label: 'Baseline', value: 'Windows 11 Security Baseline' } ] }; expect(tileBadges(other, 'purple-knight')).toEqual([]); expect(tileBadges({ ...other, fields: [] }, 'group-policy')).toEqual([]); }); it('uses the fixed legacy order independently of source-column order', () => { expect(tileBadges(record).map(badge => badge.text)).toEqual([ 'IOE Found', 'Entra ID', 'Low', 'Weight: 2', 'Score: 86' ]); }); it('only prefixes weight and score and applies severity styling to severity', () => { const badges = tileBadges(record); expect(badges.find(badge => badge.label === 'Category')?.text).toBe('Entra ID'); expect(badges.find(badge => badge.label === 'Severity')?.severity).toBe('low'); }); it('filters out Maester result message from status badge', () => { const maesterRecord: FindingRecord = { id: 'maester-1', index: 0, title: 'Ensure all users can complete multifactor authentication', fields: [ { index: 0, label: 'Severity', value: 'High' }, { index: 1, label: 'Status', value: 'Failed' }, { index: 2, label: 'Result', value: 'You have 52 of 78 users that aren\'t registered for MFA | --- | --- | Fail | ...' } ], filled: [], meta: [] }; const badges = tileBadges(maesterRecord, 'maester'); expect(badges.find(badge => badge.label === 'Status')?.text).toBe('Failed'); }); it('zeigt bei Purple Knight Status statt des SI-Verweises aus Result', () => { const projected: FindingRecord = { ...record, fields: [ { index: 0, label: 'Result', value: 'SI000067' }, { index: 1, label: 'Status', value: 'Pass' }, { index: 2, label: 'ShortName', value: 'SI000067' } ] }; expect(tileBadges(projected, 'purple-knight').find(badge => badge.label === 'Status')?.text).toBe('Passed'); }); it('zeigt abgeleitete CIS-Kategorien auch auf Finding-Kacheln', () => { const cisRecord: FindingRecord = { ...record, title: '2.2.2 Ensure a synthetic user right', fields: [{ index: 0, label: 'Rule ID', value: '2.2.2' }] }; expect(tileBadges(cisRecord, 'cis-cat').filter(badge => badge.label === 'CIS Category').map(badge => badge.text)) .toEqual(['CIS 2', 'CIS 2.2']); }); it('keeps the CIS number on the tile and offers the run report section title on hover', () => { const cisRecord: FindingRecord = { ...record, title: '2.2.2 Ensure a synthetic user right', fields: [{ index: 0, label: 'Rule ID', value: '2.2.2' }], benchmarkSectionTitles: new Map([['2', 'Synthetic Local Policies'], ['2.2', 'Synthetic User Rights Assignment']]) }; expect(tileBadges(cisRecord, 'cis-cat').filter(badge => badge.label === 'CIS Category')).toEqual([ { label: 'CIS Category', value: 'CIS 2', text: 'CIS 2', title: 'Synthetic Local Policies' }, { label: 'CIS Category', value: 'CIS 2.2', text: 'CIS 2.2', title: 'Synthetic User Rights Assignment' } ]); }); it('ordnet abgeleitete Kategorien vor Severity, Weight und Score ein', () => { const cisRecord: FindingRecord = { ...record, title: '2.2.2 Ensure a synthetic user right' }; expect(tileBadges(cisRecord, 'cis-cat').map(badge => badge.text)).toEqual([ 'IOE Found', 'Entra ID', 'CIS 2', 'CIS 2.2', 'Low', 'Weight: 2', 'Score: 86' ]); }); it('verdichtet ueberzaehlige Graphbadges zu einem sichtbaren Zaehler', () => { const badges = [ { label: 'Status', value: 'Fail', text: 'Fail' }, { label: 'Weight', value: '1.0', text: 'Weight: 1.0' }, { label: 'CIS Category', value: 'CIS 18', text: 'CIS 18' }, { label: 'CIS Category', value: 'CIS 18.10', text: 'CIS 18.10' }, { label: 'CIS Category', value: 'CIS 18.10.18', text: 'CIS 18.10.18' } ]; expect(compactTileBadges(badges, 4).map(badge => badge.text)).toEqual(['Fail', 'Weight: 1.0', 'CIS 18.10.18', '+2']); }); it('keeps long GPO registry titles inside the tile content column', () => { const title = 'HKEY_LOCAL_MACHINE · SOFTWARE\\Policies\\Microsoft\\Windows\\Windows Error Reporting\\Disabled'; const html = renderToStaticMarkup(createElement(TileCard, { record: { ...record, title }, title, bookmarked: false, toolId: 'group-policy', onOpen() {}, onBookmark() {} })); const css = readFileSync(new URL('../../src/styles/legacy-views.css', import.meta.url), 'utf8'); const gpoRule = css.slice(css.indexOf('.data-tile[data-tool="group-policy"]')).split('}')[0]; expect(html).toContain('data-tool="group-policy"'); expect(html).toContain('HKEY_LOCAL_MACHINE'); expect(gpoRule).toContain('overflow-wrap: anywhere'); expect(gpoRule).toContain('max-width: 100%'); expect(gpoRule).not.toMatch(/overflow:\s*hidden|text-overflow|nowrap/); }); });