Files
openmaic/OpenMAIC/tests/components/generation-jobs-panel.test.ts
2026-08-16 14:58:47 +08:00

125 lines
3.5 KiB
TypeScript

// @vitest-environment jsdom
import { act, createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { afterEach, describe, expect, it, vi } from 'vitest';
const mocks = vi.hoisted(() => ({
opsEnabled: true,
push: vi.fn(),
}));
vi.mock('next/navigation', () => ({
useRouter: () => ({ push: mocks.push }),
}));
vi.mock('@/lib/config/deployment-role', () => ({
isOpsWorkbenchEnabled: () => mocks.opsEnabled,
}));
vi.mock('@/lib/hooks/use-i18n', () => ({
useI18n: () => ({
t: (key: string) => key,
}),
}));
import { GenerationJobsPanel } from '@/components/classroom/generation-jobs-panel';
(
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
).IS_REACT_ACT_ENVIRONMENT = true;
let root: Root | null = null;
let container: HTMLDivElement | null = null;
async function renderPanel() {
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
await act(async () => {
root?.render(createElement(GenerationJobsPanel));
await new Promise((resolve) => setTimeout(resolve, 0));
});
}
function jsonResponse(body: unknown, status = 200): Response {
return new Response(JSON.stringify(body), {
status,
headers: { 'content-type': 'application/json' },
});
}
afterEach(async () => {
if (root) {
await act(async () => root?.unmount());
}
container?.remove();
root = null;
container = null;
mocks.opsEnabled = true;
mocks.push.mockReset();
vi.unstubAllGlobals();
});
describe('GenerationJobsPanel deployment boundary', () => {
it('does not request the operations course API on a learner deployment', async () => {
mocks.opsEnabled = false;
const fetchMock = vi.fn(async () =>
jsonResponse({
items: [
{
jobId: 'job-1',
status: 'queued',
step: 'queued',
progress: 0,
message: 'Waiting',
createdAt: '2026-08-15T00:00:00.000Z',
requirement: 'Learner single course',
scenesGenerated: 0,
done: false,
},
],
}),
);
vi.stubGlobal('fetch', fetchMock);
await renderPanel();
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith('/api/generate-classroom?limit=10');
expect(container?.textContent).toContain('Learner single course');
});
it('keeps single-courseware jobs visible when the operations course API returns 403', async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
const url = String(input);
if (url.startsWith('/api/generate-classroom')) {
return jsonResponse({
items: [
{
jobId: 'job-2',
status: 'succeeded',
step: 'done',
progress: 100,
message: 'Done',
createdAt: '2026-08-15T00:00:00.000Z',
requirement: 'Completed single course',
scenesGenerated: 3,
totalScenes: 3,
result: { classroomId: 'classroom-2', url: '/classroom/classroom-2', scenesCount: 3 },
done: true,
},
],
});
}
return jsonResponse({ success: false, errorCode: 'FORBIDDEN' }, 403);
});
vi.stubGlobal('fetch', fetchMock);
await renderPanel();
expect(fetchMock).toHaveBeenCalledWith('/api/courses?limit=5');
expect(container?.textContent).toContain('Completed single course');
expect(container?.textContent).not.toContain('jobs.loadFailed');
});
});