Files
lcbp3/frontend/components/admin/ai/__tests__/OcrSandboxPromptManager.test.tsx
T
admin db16c95019
CI / CD Pipeline / build (push) Failing after 7m26s
CI / CD Pipeline / deploy (push) Has been skipped
690617:1443 237 #01.3
2026-06-17 14:43:30 +07:00

124 lines
4.0 KiB
TypeScript

// File: frontend/components/admin/ai/__tests__/OcrSandboxPromptManager.test.tsx
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import OcrSandboxPromptManager from '../OcrSandboxPromptManager';
// Mock Hooks
vi.mock('@/hooks/use-ai-prompts', () => ({
useAiPrompts: vi.fn(() => ({
versionsQuery: { data: [], isSuccess: true, refetch: vi.fn() },
createMutation: { mutateAsync: vi.fn(), isPending: false },
activateMutation: { mutateAsync: vi.fn() },
deleteMutation: { mutateAsync: vi.fn() },
updateNoteMutation: { mutateAsync: vi.fn() },
})),
useSandboxRun: vi.fn(() => ({
state: { isRunning: false },
jobId: null,
reset: vi.fn(),
startPolling: vi.fn(),
})),
}));
vi.mock('@/hooks/use-translations', () => ({
useTranslations: vi.fn(() => (key: string) => key),
}));
vi.mock('@/hooks/use-master-data', () => ({
useProjects: vi.fn(() => ({ data: [{ publicId: 'proj-1', projectCode: 'P1', projectName: 'Project 1' }] })),
useContracts: vi.fn(() => ({ data: [] })),
}));
// Mock React Query
vi.mock('@tanstack/react-query', () => ({
useQuery: vi.fn(() => ({ data: [] })),
}));
// Mock Service
vi.mock('@/lib/services/admin-ai.service', () => ({
adminAiService: {
getOcrEngines: vi.fn().mockResolvedValue([]),
getSandboxProfile: vi.fn().mockResolvedValue({}),
getProductionDefaults: vi.fn().mockResolvedValue({}),
},
}));
vi.mock('sonner', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
},
}));
// We must mock PromptVersionHistory since it might rely on other complex contexts
vi.mock('../PromptVersionHistory', () => ({
default: () => <div data-testid="mock-prompt-version-history" />,
}));
describe('OcrSandboxPromptManager', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('renders correctly and defaults to sandbox tab', async () => {
const { container } = render(<OcrSandboxPromptManager />);
await waitFor(() => {
expect(screen.getByText('ai.prompt.sandboxCardTitle')).toBeInTheDocument();
});
expect(screen.getByText('ai.prompt.tabSandbox')).toBeInTheDocument();
expect(screen.getByText('ai.prompt.tabEditor')).toBeInTheDocument();
});
it('switches to editor tab', async () => {
const user = userEvent.setup();
render(<OcrSandboxPromptManager />);
await waitFor(() => {
expect(screen.getByText('ai.prompt.tabEditor')).toBeInTheDocument();
});
const editorTab = screen.getByText('ai.prompt.tabEditor');
await user.click(editorTab);
expect(screen.getByText('ai.prompt.cardTitle')).toBeInTheDocument();
});
it('handles rate limiting (429/503) errors gracefully on OCR submission', async () => {
const user = userEvent.setup();
const mockSubmitOcr = vi.fn().mockRejectedValue({
response: { data: { message: 'Rate limit exceeded. Try again later.' } }
});
// Override the mock implementation for this test
const { adminAiService } = await import('@/lib/services/admin-ai.service');
(adminAiService.submitSandboxOcr as any) = mockSubmitOcr;
render(<OcrSandboxPromptManager />);
// Select project
const selects = document.querySelectorAll('select');
const projectSelect = selects[0]; // First select is Project
await userEvent.selectOptions(projectSelect, 'proj-1');
// Simulate file drop/upload
const file = new File(['dummy content'], 'test.pdf', { type: 'application/pdf' });
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
await userEvent.upload(fileInput, file);
const submitButton = screen.getByRole('button', { name: /Step 1: Run OCR Only/i });
await user.click(submitButton);
await waitFor(() => {
expect(mockSubmitOcr).toHaveBeenCalled();
});
// Check if error toast was called
const { toast } = await import('sonner');
expect(toast.error).toHaveBeenCalledWith('Rate limit exceeded. Try again later.');
});
});