Assessor_2.0/tests/compliance/ai-overview-activity.test.tsx

58 lines
2.6 KiB
TypeScript

// @vitest-environment jsdom
import { act, type ReactNode } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, expect, it, vi } from 'vitest';
import type { AiOverviewResponse } from '../../src/compliance/ai-overview';
afterEach(() => {
vi.unstubAllGlobals();
vi.resetModules();
});
const base: AiOverviewResponse = {
version: 1, projectId: 'project-a', scope: { assessmentIds: ['assessment-a'], scanRunIds: [] },
generationEnabled: true, state: 'empty', stale: false
};
it('shows one generation entry and no status-poll entries while the backend generates', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
let backendState: AiOverviewResponse['state'] = 'empty';
// The real request path: the API client labels every request it logs through the activity store.
vi.stubGlobal('fetch', vi.fn(async (_url: string, init?: RequestInit) => {
if (init?.method === 'POST') backendState = 'running';
const body = { ...base, state: backendState, pollAfterMs: backendState === 'running' ? 10 : undefined };
return new Response(JSON.stringify(body), { status: 200, headers: { 'Content-Type': 'application/json' } });
}));
const { useAiOverview } = await import('../../src/compliance/ui/AiOverviewPanel');
const { activitySnapshot } = await import('../../src/platform/activity-store');
const labels = () => activitySnapshot().map(row => `${row.label}:${row.state}`);
let generate!: () => Promise<void>;
function Probe(): ReactNode {
const controller = useAiOverview('project-a', ['assessment-a']);
generate = controller.generate;
return <output>{controller.response?.state ?? 'none'}</output>;
}
const container = document.createElement('div');
document.body.append(container);
const root = createRoot(container);
try {
await act(async () => root.render(<Probe />));
expect(labels()).toEqual(['Loading AI overview status:completed']);
await act(async () => { await generate(); });
await act(async () => { await new Promise(resolve => setTimeout(resolve, 50)); });
expect(container.textContent).toBe('running');
expect(labels()).toEqual(['Loading AI overview status:completed', 'Starting AI overview:completed', 'Generating AI overview:running']);
backendState = 'succeeded';
await act(async () => { await new Promise(resolve => setTimeout(resolve, 50)); });
expect(container.textContent).toBe('succeeded');
expect(labels()).toEqual(['Loading AI overview status:completed', 'Starting AI overview:completed', 'Generating AI overview:completed']);
} finally {
await act(async () => root.unmount());
container.remove();
}
});