Assessor_2.0/tests/compliance/iso-corpus-upload-interacti...

100 lines
5.2 KiB
TypeScript

// @vitest-environment jsdom
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, expect, it, vi } from 'vitest';
import { AssessorApiClient } from '../../src/api/assessor-api-client';
import type { RegulatoryCorpusStatus } from '../../src/compliance/regulatory-corpus-management';
import { IsoCorpusLibrary } from '../../src/compliance/ui/IsoCorpusLibrary';
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); });
it('uploads both selected ISO PDFs with the entered operator token and shows embedding progress', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const initial: RegulatoryCorpusStatus = { version: 1, configured: true, active: null, job: null };
vi.spyOn(AssessorApiClient.prototype, 'getIsoCorpusStatus').mockResolvedValue(initial);
const submit = vi.spyOn(AssessorApiClient.prototype, 'submitIsoCorpus').mockResolvedValue({
...initial, job: { id: 'synthetic-job', status: 'running', phase: 'embedding', startedAt: '2026-09-23T00:00:00Z',
completedChunks: 2, totalChunks: 8 }
});
const container = document.createElement('div');
document.body.append(container);
const root = createRoot(container);
try {
await act(async () => root.render(<IsoCorpusLibrary/>));
const fileInputs = container.querySelectorAll<HTMLInputElement>('input[type="file"]');
const first = new File(['%PDF-synthetic-27001'], '27001.pdf', { type: 'application/pdf' });
const second = new File(['%PDF-synthetic-27002'], '27002.pdf', { type: 'application/pdf' });
await act(async () => {
Object.defineProperty(fileInputs[0], 'files', { configurable: true, value: [first] });
fileInputs[0].dispatchEvent(new Event('change', { bubbles: true }));
Object.defineProperty(fileInputs[1], 'files', { configurable: true, value: [second] });
fileInputs[1].dispatchEvent(new Event('change', { bubbles: true }));
const token = container.querySelector<HTMLInputElement>('input[type="password"]')!;
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')!.set!.call(token, 'synthetic-secret');
token.dispatchEvent(new Event('input', { bubbles: true }));
});
await act(async () => container.querySelector('form')!.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })));
expect(submit).toHaveBeenCalledWith(first, second, 'synthetic-secret');
expect(container.textContent).toContain('Embedding 2 / 8');
expect(container.querySelector<HTMLInputElement>('input[type="password"]')!.value).toBe('');
} finally {
await act(async () => root.unmount());
container.remove();
}
});
it('clears a transient status polling error once a later poll succeeds', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
vi.useFakeTimers();
const job = { id: 'synthetic-job', status: 'running' as const, phase: 'embedding' as const, startedAt: '2026-09-23T00:00:00Z' };
vi.spyOn(AssessorApiClient.prototype, 'getIsoCorpusStatus')
.mockResolvedValueOnce({ version: 1, configured: true, active: null, job })
.mockRejectedValueOnce(new Error('Synthetic transient failure.'))
.mockResolvedValue({ version: 1, configured: true, active: null, job: { ...job, status: 'succeeded', phase: 'complete' } });
const container = document.createElement('div');
document.body.append(container);
const root = createRoot(container);
try {
await act(async () => root.render(<IsoCorpusLibrary/>));
await act(async () => { await vi.advanceTimersByTimeAsync(2_000); });
expect(container.textContent).toContain('Synthetic transient failure.');
await act(async () => { await vi.advanceTimersByTimeAsync(2_000); });
expect(container.textContent).not.toContain('Synthetic transient failure.');
expect(container.textContent).toContain('ISO corpus indexed and active.');
} finally {
await act(async () => root.unmount());
container.remove();
vi.useRealTimers();
}
});
it('switches automatic mapping of new scans on and off from the Data Hub ISO tile', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const off: RegulatoryCorpusStatus = { version: 1, configured: true, active: null, job: null, autoMapping: { enabled: false } };
vi.spyOn(AssessorApiClient.prototype, 'getIsoCorpusStatus').mockResolvedValue(off);
const toggle = vi.spyOn(AssessorApiClient.prototype, 'setIsoAutoMapping')
.mockImplementation(async enabled => ({ ...off, autoMapping: { enabled } }));
const container = document.createElement('div');
document.body.append(container);
const root = createRoot(container);
try {
await act(async () => root.render(<IsoCorpusLibrary/>));
const checkbox = () => container.querySelector<HTMLInputElement>('.iso-auto-mapping-toggle input[type="checkbox"]')!;
expect(container.querySelector('.iso-auto-mapping-toggle')?.textContent).toBe('Map new scans automatically');
expect(checkbox().checked).toBe(false);
await act(async () => checkbox().click());
expect(toggle).toHaveBeenLastCalledWith(true);
expect(checkbox().checked).toBe(true);
await act(async () => checkbox().click());
expect(toggle).toHaveBeenLastCalledWith(false);
expect(checkbox().checked).toBe(false);
} finally {
await act(async () => root.unmount());
container.remove();
}
});