58 lines
2.6 KiB
TypeScript
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();
|
|
}
|
|
});
|