77 lines
5.1 KiB
TypeScript
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(); }
|
|
});
|
|
});
|