106 lines
6.0 KiB
TypeScript
106 lines
6.0 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { act } from 'react';
|
|
import { createRoot, type Root } from 'react-dom/client';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { AssessorApiClient } from '../../src/api/assessor-api-client';
|
|
import { resetEnginePreferencesForTests, setEngineEnabled } from '../../src/app/engine-preferences';
|
|
import type { BsiCatalogState } from '../../src/compliance/bsi-catalog-management';
|
|
import { BsiCatalogLibrary } from '../../src/compliance/ui/BsiCatalogLibrary';
|
|
import { useRegulatoryDecisionRevision } from '../../src/compliance/ui/decision-revision';
|
|
|
|
// Data Hub BSI tile (user decision 2026-10-08): like the MITRE matrix tile, it names the active catalog and
|
|
// mapping and offers `Update from BSI`; `Use bundled catalog` appears only after an update. Synthetic states.
|
|
const bundled: BsiCatalogState = { version: 1, origin: 'bundled', catalogVersion: '2026-09-24T07:12:39.131983+00:00', crosswalkVersion: '1.0.1-qa',
|
|
requirementCount: 901, crosswalkEntryCount: 83, catalogSha256: 'a'.repeat(64), crosswalkSha256: 'b'.repeat(64), missingTargets: ['BER.6.8'] };
|
|
const updated: BsiCatalogState = { ...bundled, origin: 'bsi', updatedAt: '2026-10-08T12:00:00.000Z', catalogVersion: '2026-10-01T00:00:00+00:00',
|
|
crosswalkVersion: '1.0.2', missingTargets: ['BER.6.8', 'GEB.5.4'] };
|
|
|
|
let host: HTMLDivElement;
|
|
let root: Root;
|
|
let revisions: number[];
|
|
function Revision() { revisions.push(useRegulatoryDecisionRevision()); return null; }
|
|
const render = () => act(async () => {
|
|
root.render(<><BsiCatalogLibrary/><Revision/></>);
|
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
});
|
|
const badge = () => host.querySelector('[data-bsi-catalog] .datahub-rack-badge')?.textContent;
|
|
const button = (label: string) => [...host.querySelectorAll<HTMLButtonElement>('[data-bsi-catalog] button')].find(item => item.textContent === label);
|
|
|
|
beforeEach(() => {
|
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
|
localStorage.clear();
|
|
resetEnginePreferencesForTests();
|
|
revisions = [];
|
|
host = document.createElement('div');
|
|
document.body.append(host);
|
|
root = createRoot(host);
|
|
});
|
|
afterEach(async () => {
|
|
await act(async () => root.unmount());
|
|
host.remove();
|
|
vi.restoreAllMocks();
|
|
vi.unstubAllGlobals();
|
|
resetEnginePreferencesForTests();
|
|
});
|
|
|
|
describe('Data Hub BSI catalog tile', () => {
|
|
it('names the bundled catalog and mapping, updates from BSI and reloads the derived BSI views', async () => {
|
|
vi.spyOn(AssessorApiClient.prototype, 'getBsiCatalog').mockResolvedValue(bundled);
|
|
const refresh = vi.spyOn(AssessorApiClient.prototype, 'refreshBsiCatalog').mockResolvedValue({ ...updated, updated: true });
|
|
await render();
|
|
expect(host.querySelector('[data-bsi-catalog] .datahub-rack-header strong')?.textContent).toBe('BSI GRUNDSCHUTZ++ KERNEL');
|
|
expect(badge()).toBe('2026-09-24 · Mapping 1.0.1-qa · Bundled');
|
|
expect(button('Use bundled catalog')).toBeUndefined();
|
|
const before = revisions.at(-1)!;
|
|
await act(async () => button('Update from BSI')!.click());
|
|
expect(refresh).toHaveBeenCalledTimes(1);
|
|
expect(badge()).toBe('2026-10-01 · Mapping 1.0.2 · Updated');
|
|
expect(host.querySelector('[role="status"]')?.textContent).toBe('✓Updated to catalog 2026-10-01 and mapping 1.0.2. 2 mapping targets are not in the catalog.');
|
|
// Client Info and Work reload their derived BSI requirements.
|
|
expect(revisions.at(-1)).toBe(before + 1);
|
|
expect(button('Use bundled catalog')).toBeDefined();
|
|
});
|
|
|
|
it('says when the catalog is up to date without reloading other views', async () => {
|
|
vi.spyOn(AssessorApiClient.prototype, 'getBsiCatalog').mockResolvedValue(bundled);
|
|
vi.spyOn(AssessorApiClient.prototype, 'refreshBsiCatalog').mockResolvedValue({ ...bundled, updated: false });
|
|
await render();
|
|
const before = revisions.at(-1);
|
|
await act(async () => button('Update from BSI')!.click());
|
|
expect(host.querySelector('[role="status"]')?.textContent).toBe('✓The BSI catalog is up to date.');
|
|
expect(revisions.at(-1)).toBe(before);
|
|
});
|
|
|
|
it('returns to the bundled catalog and shows an update failure without changing the badge', async () => {
|
|
vi.spyOn(AssessorApiClient.prototype, 'getBsiCatalog').mockResolvedValue(updated);
|
|
const reset = vi.spyOn(AssessorApiClient.prototype, 'resetBsiCatalog').mockResolvedValue(bundled);
|
|
vi.spyOn(AssessorApiClient.prototype, 'refreshBsiCatalog').mockRejectedValue(new Error('BSI could not be reached. Please try again later.'));
|
|
await render();
|
|
await act(async () => button('Update from BSI')!.click());
|
|
expect(host.querySelector('[role="alert"]')?.textContent).toBe('BSI could not be reached. Please try again later.');
|
|
expect(badge()).toBe('2026-10-01 · Mapping 1.0.2 · Updated');
|
|
await act(async () => button('Use bundled catalog')!.click());
|
|
expect(reset).toHaveBeenCalledTimes(1);
|
|
expect(badge()).toBe('2026-09-24 · Mapping 1.0.1-qa · Bundled');
|
|
expect(host.querySelector('[role="status"]')?.textContent).toBe('✓The bundled BSI catalog is active again.');
|
|
expect(button('Use bundled catalog')).toBeUndefined();
|
|
});
|
|
|
|
it('renders and requests nothing while the Compliance Engine is off', async () => {
|
|
const request = vi.spyOn(AssessorApiClient.prototype, 'getBsiCatalog').mockResolvedValue(bundled);
|
|
setEngineEnabled('compliance', false);
|
|
await render();
|
|
expect(host.querySelector('[data-bsi-catalog]')).toBeNull();
|
|
expect(request).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('sits in the Data Hub reference row directly after the ISO corpus tile', () => {
|
|
const source = readFileSync(join(process.cwd(), 'src/features/project-import/DataHub.tsx'), 'utf8');
|
|
expect(source).toContain(' <IsoCorpusLibrary/>\n <BsiCatalogLibrary/>\n');
|
|
const styles = readFileSync(join(process.cwd(), 'src/styles/datahub-redesign.css'), 'utf8');
|
|
expect(styles).toContain('.datahub-reference-row > .bsi-catalog-library { grid-area: 4 / 1 / 5 / -1; }');
|
|
});
|
|
});
|