Assessor_2.0/tests/features/maester-category-badges.tes...

77 lines
5.1 KiB
TypeScript

// @vitest-environment jsdom
import { act, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, describe, expect, it, vi } from 'vitest';
import type { FindingRecord } from '../../src/domain/finding';
import { TileCard } from '../../src/components/TileCard/TileCard';
import { FindingReader } from '../../src/features/finding-reader/FindingReader';
const record: FindingRecord = {
id: 'synthetic-removable-scan', index: 0, title: 'Synthetic removable drive scan check', filled: [], meta: [],
fields: [['Source: Scan', 'Maester'], ['Source Rule ID', 'MT.1154'], ['Result', 'Failed'],
['Severity', 'High'], ['Block', 'Maester/Defender'], ['Tag', '["Maester","Defender","MT.1154"]'],
['Description', 'Synthetic scanner description']].map(([label, value], index) => ({ label, value, index }))
};
const noop = () => {};
function WorkReading({ finding, toolId }: { finding: FindingRecord; toolId: string }) {
const [opened, setOpened] = useState(false);
return opened ? <><button onClick={() => setOpened(false)}>Back to tiles</button>
<FindingReader record={finding} title={finding.title} toolId={toolId} linkedSheets={[]} resultReferences={[]}
search="" position={0} recordCount={1} onPrevious={noop} onNext={noop} onReview={noop}/></>
: <TileCard record={finding} title={finding.title} toolId={toolId} badgeLimit={3} bookmarked={false}
onOpen={() => setOpened(true)} onBookmark={noop}/>;
}
afterEach(() => { vi.unstubAllGlobals(); document.body.replaceChildren(); });
describe.each(['light', 'dark'])('Native Maester category badges (%s)', theme => {
it.each(['maester', 'generic'])('keeps the category visible when opening compact tiles by mouse and keyboard in a %s workspace', async toolId => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
document.documentElement.dataset.theme = theme;
const original = JSON.stringify(record);
const host = document.createElement('div'); document.body.append(host); const root = createRoot(host);
try {
await act(async () => root.render(<WorkReading finding={record} toolId={toolId}/>));
for (const activation of ['mouse', 'keyboard']) {
const tile = host.querySelector<HTMLElement>('.data-tile')!;
expect(tile.querySelector('[data-badge-role="category"]')?.textContent).toBe('Maester/Defender');
expect(tile.querySelector('[data-badge-role="status"]')?.textContent).toBe('Failed');
expect([...tile.querySelector('.data-tile-badges')!.children].map(badge => badge.textContent)).toEqual(['Failed', 'High', 'Maester/Defender']);
await act(async () => {
if (activation === 'mouse') tile.click();
else tile.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
});
const badges = host.querySelector('.finding-badges')!;
expect([...badges.querySelectorAll('.status-badge')].map(badge => badge.textContent)).toEqual(['Category: Maester/Defender']);
expect(badges.querySelector('.severity-badge')?.textContent).toBe('High');
expect(badges.querySelector('.outcome-mark')?.textContent).toBe('Failed');
expect([...badges.children].map(badge => badge.textContent)).toEqual(['Failed', 'High', 'Category: Maester/Defender']);
expect(host.querySelector('.reader-summary')?.textContent).toContain('CategoryMaester/Defender');
expect(JSON.stringify(record)).toBe(original);
await act(async () => host.querySelector<HTMLButtonElement>('button')!.click());
}
} finally { await act(async () => root.unmount()); host.remove(); }
});
it('preserves explicit categories and does not turn missing or non-Maester Block fields into categories', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
document.documentElement.dataset.theme = theme;
const host = document.createElement('div'); document.body.append(host); const root = createRoot(host);
const cases = [
{ finding: { ...record, fields: [...record.fields, { label: 'Category', value: 'Explicit scanner category', index: 8 }] }, toolId: 'maester', category: 'Explicit scanner category' },
{ finding: { ...record, fields: record.fields.map(field => field.label === 'Block' ? { ...field, value: ' ' } : field) }, toolId: 'maester', category: undefined },
{ finding: { ...record, fields: record.fields.filter(field => field.label !== 'Source: Scan') }, toolId: 'purple-knight', category: undefined }
];
try {
for (const { finding, toolId, category } of cases) {
await act(async () => root.render(<WorkReading key={`${toolId}:${category}`} finding={finding} toolId={toolId}/>));
expect(host.querySelector('[data-badge-role="category"]')?.textContent).toBe(category);
await act(async () => host.querySelector<HTMLElement>('.data-tile')!.click());
const categoryBadge = [...host.querySelectorAll('.finding-badges .status-badge')].find(badge => badge.textContent?.startsWith('Category:'));
expect(categoryBadge?.textContent).toBe(category ? `Category: ${category}` : undefined);
}
} finally { await act(async () => root.unmount()); host.remove(); }
});
});