fix(tests): resolve quite a few broen tests
This commit is contained in:
parent
7fb1116695
commit
8d181146a2
|
|
@ -1,7 +1,23 @@
|
|||
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
import Login from '../Login';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../../test/test-utils';
|
||||
|
||||
// Mock AuthContext to work with the test setup
|
||||
vi.mock('../../../contexts/AuthContext', () => ({
|
||||
useAuth: vi.fn(() => ({
|
||||
user: null,
|
||||
loading: false,
|
||||
login: vi.fn().mockResolvedValue({}),
|
||||
register: vi.fn().mockResolvedValue({}),
|
||||
logout: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock ThemeContext
|
||||
vi.mock('../../../contexts/ThemeContext', () => ({
|
||||
useTheme: () => ({
|
||||
darkMode: false,
|
||||
toggleDarkMode: vi.fn()
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock the API
|
||||
vi.mock('../../../services/api', () => ({
|
||||
|
|
@ -25,6 +41,11 @@ vi.mock('react-router-dom', async () => {
|
|||
};
|
||||
});
|
||||
|
||||
// Now import after all mocks are set up
|
||||
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { renderWithProviders, createMockUser } from '../../../test/test-utils';
|
||||
import Login from '../Login';
|
||||
|
||||
// Mock window.location
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
|
|
@ -36,7 +57,6 @@ Object.defineProperty(window, 'location', {
|
|||
describe('Login - OIDC Features', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setupTestEnvironment();
|
||||
});
|
||||
|
||||
const renderLogin = () => {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,46 @@
|
|||
import { screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { vi } from 'vitest';
|
||||
import OidcCallback from '../OidcCallback';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../../test/test-utils';
|
||||
import { api } from '../../../services/api';
|
||||
import React from 'react';
|
||||
|
||||
// Mock the API
|
||||
vi.mock('../../../services/api', () => ({
|
||||
api: {
|
||||
get: vi.fn(),
|
||||
defaults: {
|
||||
headers: {
|
||||
common: {}
|
||||
}
|
||||
// Create stable mock functions
|
||||
const mockLogin = vi.fn().mockResolvedValue({});
|
||||
const mockRegister = vi.fn().mockResolvedValue({});
|
||||
const mockLogout = vi.fn();
|
||||
|
||||
// Mock the auth context module completely
|
||||
vi.mock('../../../contexts/AuthContext', () => ({
|
||||
useAuth: vi.fn(() => ({
|
||||
user: null,
|
||||
loading: false,
|
||||
login: mockLogin,
|
||||
register: mockRegister,
|
||||
logout: mockLogout,
|
||||
})),
|
||||
AuthProvider: ({ children }: { children: React.ReactNode }) => React.createElement('div', null, children),
|
||||
}));
|
||||
|
||||
// Mock axios comprehensively to prevent any real HTTP requests
|
||||
import { createComprehensiveAxiosMock, createComprehensiveApiMocks } from '../../../test/comprehensive-mocks';
|
||||
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Create the mock API object
|
||||
const mockApi = {
|
||||
get: vi.fn().mockResolvedValue({ data: { token: 'default-token' } }),
|
||||
post: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
put: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
delete: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
patch: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
defaults: {
|
||||
headers: {
|
||||
common: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Mock the services/api file
|
||||
vi.mock('../../../services/api', () => ({
|
||||
api: mockApi,
|
||||
default: mockApi,
|
||||
}));
|
||||
|
||||
// Mock useNavigate
|
||||
|
|
@ -28,6 +54,11 @@ vi.mock('react-router-dom', async () => {
|
|||
};
|
||||
});
|
||||
|
||||
// Now import after mocks
|
||||
import { screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { renderWithProviders } from '../../../test/test-utils';
|
||||
import OidcCallback from '../OidcCallback';
|
||||
|
||||
// Mock window.location
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
|
|
@ -39,28 +70,29 @@ Object.defineProperty(window, 'location', {
|
|||
describe('OidcCallback', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setupTestEnvironment();
|
||||
vi.resetModules();
|
||||
window.location.href = '';
|
||||
// Clear API mocks
|
||||
(api.get as any).mockClear();
|
||||
mockApi.get.mockClear();
|
||||
// Reset API mocks to default implementation
|
||||
(api.get as any).mockResolvedValue({ data: { token: 'default-token' } });
|
||||
mockApi.get.mockResolvedValue({ data: { token: 'default-token' } });
|
||||
});
|
||||
|
||||
const renderOidcCallback = (search = '') => {
|
||||
return renderWithProviders(
|
||||
<MemoryRouter initialEntries={[`/auth/oidc/callback${search}`]}>
|
||||
<Routes>
|
||||
<Route path="/auth/oidc/callback" element={<OidcCallback />} />
|
||||
<Route path="/login" element={<div>Login Page</div>} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
// Mock the URL search params for the component
|
||||
const url = new URL(`http://localhost/auth/oidc/callback${search}`);
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { search: url.search },
|
||||
writable: true
|
||||
});
|
||||
|
||||
// Use renderWithProviders to get auth context
|
||||
return renderWithProviders(<OidcCallback />);
|
||||
};
|
||||
|
||||
it('shows loading state initially', async () => {
|
||||
// Mock the API call to delay so we can see the loading state
|
||||
(api.get as any).mockImplementation(() => new Promise(() => {})); // Never resolves
|
||||
mockApi.get.mockImplementation(() => new Promise(() => {})); // Never resolves
|
||||
|
||||
renderOidcCallback('?code=test-code&state=test-state');
|
||||
|
||||
|
|
@ -80,12 +112,12 @@ describe('OidcCallback', () => {
|
|||
}
|
||||
};
|
||||
|
||||
(api.get as any).mockResolvedValueOnce(mockResponse);
|
||||
mockApi.get.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderOidcCallback('?code=test-code&state=test-state');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.get).toHaveBeenCalledWith('/auth/oidc/callback?code=test-code&state=test-state');
|
||||
expect(mockApi.get).toHaveBeenCalledWith('/auth/oidc/callback?code=test-code&state=test-state');
|
||||
});
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith('token', 'test-jwt-token');
|
||||
|
|
@ -114,7 +146,7 @@ describe('OidcCallback', () => {
|
|||
}
|
||||
}
|
||||
};
|
||||
(api.get as any).mockRejectedValueOnce(error);
|
||||
mockApi.get.mockRejectedValueOnce(error);
|
||||
|
||||
renderOidcCallback('?code=test-code&state=test-state');
|
||||
|
||||
|
|
@ -125,7 +157,7 @@ describe('OidcCallback', () => {
|
|||
});
|
||||
|
||||
it('handles invalid response from server', async () => {
|
||||
(api.get as any).mockResolvedValueOnce({
|
||||
mockApi.get.mockResolvedValueOnce({
|
||||
data: {
|
||||
// Missing token
|
||||
user: { id: '123' }
|
||||
|
|
@ -141,7 +173,7 @@ describe('OidcCallback', () => {
|
|||
});
|
||||
|
||||
it('provides return to login button on error', async () => {
|
||||
(api.get as any).mockRejectedValueOnce(new Error('Network error'));
|
||||
mockApi.get.mockRejectedValueOnce(new Error('Network error'));
|
||||
|
||||
renderOidcCallback('?code=test-code&state=test-state');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
import React from 'react';
|
||||
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { vi } from 'vitest';
|
||||
import GlobalSearchBar from '../GlobalSearchBar';
|
||||
import { renderWithProviders, createMockApiServices, setupTestEnvironment, createMockLocalStorage } from '../../../test/test-utils';
|
||||
import { createComprehensiveAxiosMock, createComprehensiveApiMocks } from '../../../test/comprehensive-mocks';
|
||||
|
||||
// Use centralized API mocking
|
||||
const mockServices = createMockApiServices();
|
||||
const mockDocumentService = mockServices.documentService;
|
||||
const localStorageMock = createMockLocalStorage();
|
||||
// Mock axios comprehensively to prevent any real HTTP requests
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Mock API services comprehensively
|
||||
vi.mock('../../../services/api', async () => {
|
||||
const actual = await vi.importActual('../../../services/api');
|
||||
const apiMocks = createComprehensiveApiMocks();
|
||||
|
||||
return {
|
||||
...actual,
|
||||
...apiMocks,
|
||||
};
|
||||
});
|
||||
|
||||
// Mock useNavigate
|
||||
const mockNavigate = vi.fn();
|
||||
|
|
@ -20,6 +25,18 @@ vi.mock('react-router-dom', async () => {
|
|||
};
|
||||
});
|
||||
|
||||
// Import after mocking
|
||||
import React from 'react';
|
||||
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import GlobalSearchBar from '../GlobalSearchBar';
|
||||
import { renderWithProviders, createMockApiServices, createMockLocalStorage } from '../../../test/test-utils';
|
||||
|
||||
// Use centralized API mocking
|
||||
const mockServices = createMockApiServices();
|
||||
const mockDocumentService = mockServices.documentService;
|
||||
const localStorageMock = createMockLocalStorage();
|
||||
|
||||
// Mock data
|
||||
const mockSearchResponse = {
|
||||
data: {
|
||||
|
|
@ -45,7 +62,6 @@ const mockSearchResponse = {
|
|||
describe('GlobalSearchBar', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setupTestEnvironment();
|
||||
localStorageMock.getItem.mockReturnValue(null);
|
||||
mockDocumentService.enhancedSearch.mockResolvedValue(mockSearchResponse);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import Label, { type LabelData } from '../Label';
|
|||
import { renderWithProviders } from '../../../test/test-utils';
|
||||
import {
|
||||
createMockLabel,
|
||||
createMockSystemLabel,
|
||||
setupTestEnvironment
|
||||
createMockSystemLabel
|
||||
} from '../../../test/label-test-utils';
|
||||
|
||||
const mockLabel = createMockLabel({
|
||||
|
|
@ -31,7 +30,7 @@ const renderLabel = (props: Partial<React.ComponentProps<typeof Label>> = {}) =>
|
|||
|
||||
describe('Label Component', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
// Test setup is handled globally
|
||||
});
|
||||
|
||||
describe('Basic Rendering', () => {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import LabelCreateDialog from '../LabelCreateDialog';
|
||||
import { type LabelData } from '../Label';
|
||||
import { renderWithProviders } from '../../../test/test-utils';
|
||||
import {
|
||||
createMockLabel,
|
||||
setupTestEnvironment,
|
||||
labelValidationScenarios
|
||||
} from '../../../test/label-test-utils';
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
const mockEditingLabel = createMockLabel({
|
||||
name: 'Existing Label',
|
||||
description: 'An existing label',
|
||||
|
|
@ -33,7 +35,6 @@ describe('LabelCreateDialog Component', () => {
|
|||
let user: ReturnType<typeof userEvent.setup>;
|
||||
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import LabelSelector from '../LabelSelector';
|
|||
import { type LabelData } from '../Label';
|
||||
import { renderWithProviders } from '../../../test/test-utils';
|
||||
import {
|
||||
setupTestEnvironment,
|
||||
testDataBuilders
|
||||
} from '../../../test/label-test-utils';
|
||||
|
||||
|
|
@ -26,7 +25,6 @@ describe('LabelSelector Component', () => {
|
|||
let user: ReturnType<typeof userEvent.setup>;
|
||||
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
|
|
@ -81,7 +79,7 @@ describe('LabelSelector Component', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Work')).toBeInTheDocument();
|
||||
expect(screen.getByText('Personal Project')).toBeInTheDocument();
|
||||
expect(screen.getByText('Project Alpha')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Important should not appear in the dropdown options (but may appear in selected tags)
|
||||
|
|
@ -141,7 +139,7 @@ describe('LabelSelector Component', () => {
|
|||
test('should remove label when delete button is clicked', async () => {
|
||||
const onLabelsChange = vi.fn();
|
||||
// Use only non-system labels since system labels don't have delete buttons
|
||||
const selectedLabels = [mockLabels[2]]; // Personal Project (non-system)
|
||||
const selectedLabels = [mockLabels[3]]; // Project Alpha (non-system)
|
||||
|
||||
renderLabelSelector({
|
||||
selectedLabels,
|
||||
|
|
@ -149,7 +147,7 @@ describe('LabelSelector Component', () => {
|
|||
});
|
||||
|
||||
// Find the chip with the delete button
|
||||
const personalProjectChip = screen.getByText('Personal Project').closest('.MuiChip-root');
|
||||
const personalProjectChip = screen.getByText('Project Alpha').closest('.MuiChip-root');
|
||||
expect(personalProjectChip).toBeInTheDocument();
|
||||
|
||||
// Find the delete button within that specific chip
|
||||
|
|
@ -164,7 +162,7 @@ describe('LabelSelector Component', () => {
|
|||
});
|
||||
|
||||
test('should not show delete buttons when disabled', () => {
|
||||
const selectedLabels = [mockLabels[2]]; // Non-system label
|
||||
const selectedLabels = [mockLabels[3]]; // Non-system label
|
||||
|
||||
renderLabelSelector({
|
||||
selectedLabels,
|
||||
|
|
@ -186,7 +184,7 @@ describe('LabelSelector Component', () => {
|
|||
// Check that labels appear in the dropdown
|
||||
expect(screen.getByText('Important')).toBeInTheDocument();
|
||||
expect(screen.getByText('Work')).toBeInTheDocument();
|
||||
expect(screen.getByText('Personal Project')).toBeInTheDocument();
|
||||
expect(screen.getByText('Project Alpha')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -200,7 +198,7 @@ describe('LabelSelector Component', () => {
|
|||
await waitFor(() => {
|
||||
expect(screen.getByText('Important')).toBeInTheDocument();
|
||||
expect(screen.getByText('Work')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Personal Project')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Project Alpha')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -215,7 +213,7 @@ describe('LabelSelector Component', () => {
|
|||
await waitFor(() => {
|
||||
expect(screen.getByText('Work')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Important')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Personal Project')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Project Alpha')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -376,10 +374,10 @@ describe('LabelSelector Component', () => {
|
|||
await user.click(input);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Personal Project')).toBeInTheDocument();
|
||||
expect(screen.getByText('Project Alpha')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(screen.getByText('Personal Project'));
|
||||
await user.click(screen.getByText('Project Alpha'));
|
||||
|
||||
// Should not add the third label due to maxTags limit
|
||||
expect(onLabelsChange).not.toHaveBeenCalled();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderWithProviders } from '../../../test/test-utils';
|
||||
import NotificationPanel from '../NotificationPanel';
|
||||
import { NotificationProvider } from '../../../contexts/NotificationContext';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../../test/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
// Mock date-fns
|
||||
|
|
@ -28,7 +28,6 @@ const createMockAnchorEl = () => {
|
|||
|
||||
describe('NotificationPanel - Simple Tests', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
});
|
||||
test('should not render when anchorEl is null', () => {
|
||||
const { container } = renderWithProviders(
|
||||
|
|
|
|||
|
|
@ -1,33 +1,30 @@
|
|||
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { createComprehensiveAxiosMock, createComprehensiveApiMocks } from '../../../test/comprehensive-mocks';
|
||||
|
||||
// Mock axios comprehensively to prevent any real HTTP requests
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Mock API services comprehensively
|
||||
vi.mock('../../../services/api', async () => {
|
||||
const actual = await vi.importActual('../../../services/api');
|
||||
const apiMocks = createComprehensiveApiMocks();
|
||||
|
||||
return {
|
||||
...actual,
|
||||
...apiMocks,
|
||||
};
|
||||
});
|
||||
|
||||
// Import after mocking
|
||||
import { screen, fireEvent, act, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import UploadZone from '../UploadZone';
|
||||
import { renderWithProviders, setupTestEnvironment, createMockApiServices } from '../../../test/test-utils';
|
||||
import { renderWithProviders, createMockApiServices } from '../../../test/test-utils';
|
||||
import { createMockLabel } from '../../../test/label-test-utils';
|
||||
|
||||
// Setup centralized API mocks for this component
|
||||
const mockApiServices = createMockApiServices();
|
||||
|
||||
// Mock axios directly with our mock labels
|
||||
vi.mock('axios', () => ({
|
||||
default: {
|
||||
create: vi.fn(() => ({
|
||||
get: vi.fn().mockResolvedValue({
|
||||
status: 200,
|
||||
data: [createMockLabel({
|
||||
name: 'Test Label',
|
||||
color: '#0969da',
|
||||
document_count: 0,
|
||||
source_count: 0,
|
||||
})]
|
||||
}),
|
||||
post: vi.fn().mockResolvedValue({ status: 201, data: {} }),
|
||||
put: vi.fn().mockResolvedValue({ status: 200, data: {} }),
|
||||
delete: vi.fn().mockResolvedValue({ status: 204 }),
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
const mockProps = {
|
||||
onUploadComplete: vi.fn(),
|
||||
};
|
||||
|
|
@ -37,7 +34,6 @@ describe('UploadZone', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setupTestEnvironment();
|
||||
// Suppress console.error for "Failed to fetch labels" during tests
|
||||
originalConsoleError = console.error;
|
||||
console.error = vi.fn().mockImplementation((message, ...args) => {
|
||||
|
|
@ -67,7 +63,7 @@ describe('UploadZone', () => {
|
|||
});
|
||||
|
||||
test('shows accepted file types in UI', async () => {
|
||||
await renderWithProvider(<UploadZone {...mockProps} />);
|
||||
await renderWithProviders(<UploadZone {...mockProps} />);
|
||||
|
||||
// Wait for component to load
|
||||
await waitFor(() => {
|
||||
|
|
@ -79,7 +75,7 @@ describe('UploadZone', () => {
|
|||
});
|
||||
|
||||
test('displays max file size limit', async () => {
|
||||
await renderWithProvider(<UploadZone {...mockProps} />);
|
||||
await renderWithProviders(<UploadZone {...mockProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/maximum file size/i)).toBeInTheDocument();
|
||||
|
|
@ -89,7 +85,7 @@ describe('UploadZone', () => {
|
|||
});
|
||||
|
||||
test('shows browse files button', async () => {
|
||||
await renderWithProvider(<UploadZone {...mockProps} />);
|
||||
await renderWithProviders(<UploadZone {...mockProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
const browseButton = screen.getByRole('button', { name: /choose files/i });
|
||||
|
|
@ -161,7 +157,7 @@ describe('UploadZone', () => {
|
|||
|
||||
test('handles click to browse files', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderWithProvider(<UploadZone {...mockProps} />);
|
||||
await renderWithProviders(<UploadZone {...mockProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
const browseButton = screen.getByRole('button', { name: /choose files/i });
|
||||
|
|
@ -178,7 +174,7 @@ describe('UploadZone', () => {
|
|||
});
|
||||
|
||||
test('renders upload zone structure correctly', async () => {
|
||||
await renderWithProvider(<UploadZone {...mockProps} />);
|
||||
await renderWithProviders(<UploadZone {...mockProps} />);
|
||||
|
||||
// Wait for component to load
|
||||
await waitFor(() => {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,48 @@
|
|||
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import FailedDocumentViewer from '../FailedDocumentViewer';
|
||||
import { api } from '../../services/api';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../test/test-utils';
|
||||
|
||||
// Mock the API
|
||||
// Create mock function before imports
|
||||
const mockApiGet = vi.fn();
|
||||
|
||||
// Mock the api module before importing anything else
|
||||
vi.mock('../../services/api', () => ({
|
||||
api: {
|
||||
get: vi.fn(),
|
||||
},
|
||||
get: mockApiGet,
|
||||
post: vi.fn(),
|
||||
put: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
patch: vi.fn(),
|
||||
defaults: { headers: { common: {} } },
|
||||
create: vi.fn(),
|
||||
interceptors: {
|
||||
request: { use: vi.fn(), eject: vi.fn() },
|
||||
response: { use: vi.fn(), eject: vi.fn() }
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Import after mocking
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import FailedDocumentViewer from '../FailedDocumentViewer';
|
||||
import { renderWithProviders } from '../../test/test-utils';
|
||||
const theme = createTheme();
|
||||
|
||||
// Mock URL constructor with static methods
|
||||
const mockCreateObjectURL = vi.fn(() => 'mock-object-url');
|
||||
const mockRevokeObjectURL = vi.fn();
|
||||
|
||||
global.URL = class URL {
|
||||
constructor(url) {
|
||||
this.href = url;
|
||||
this.protocol = 'http:';
|
||||
this.hostname = 'localhost';
|
||||
this.pathname = '/';
|
||||
this.search = '';
|
||||
}
|
||||
|
||||
static createObjectURL = mockCreateObjectURL;
|
||||
static revokeObjectURL = mockRevokeObjectURL;
|
||||
} as any;
|
||||
|
||||
const defaultProps = {
|
||||
failedDocumentId: 'test-failed-doc-id',
|
||||
|
|
@ -26,23 +58,19 @@ const renderFailedDocumentViewer = (props = {}) => {
|
|||
);
|
||||
};
|
||||
|
||||
// Mock Blob and URL.createObjectURL
|
||||
// Mock Blob
|
||||
const mockBlob = vi.fn(() => ({
|
||||
text: () => Promise.resolve('mock text content'),
|
||||
}));
|
||||
global.Blob = mockBlob as any;
|
||||
|
||||
const mockCreateObjectURL = vi.fn(() => 'mock-object-url');
|
||||
const mockRevokeObjectURL = vi.fn();
|
||||
global.URL = {
|
||||
createObjectURL: mockCreateObjectURL,
|
||||
revokeObjectURL: mockRevokeObjectURL,
|
||||
} as any;
|
||||
|
||||
describe('FailedDocumentViewer', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.clearAllMocks();
|
||||
// Set default mock response
|
||||
mockApiGet.mockResolvedValue({
|
||||
data: new Blob(['mock document content'], { type: 'application/pdf' })
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -52,7 +80,7 @@ describe('FailedDocumentViewer', () => {
|
|||
describe('Loading State', () => {
|
||||
test('should show loading spinner initially', () => {
|
||||
// Mock API to never resolve
|
||||
vi.mocked(api.get).mockImplementation(() => new Promise(() => {}));
|
||||
mockApiGet.mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -60,7 +88,7 @@ describe('FailedDocumentViewer', () => {
|
|||
});
|
||||
|
||||
test('should show loading spinner with correct styling', () => {
|
||||
vi.mocked(api.get).mockImplementation(() => new Promise(() => {}));
|
||||
mockApiGet.mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -79,12 +107,12 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock pdf content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.get).toHaveBeenCalledWith('/documents/failed/test-failed-doc-id/view', {
|
||||
expect(mockApiGet).toHaveBeenCalledWith('/documents/failed/test-failed-doc-id/view', {
|
||||
responseType: 'blob'
|
||||
});
|
||||
});
|
||||
|
|
@ -102,7 +130,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock image content'], { type: 'image/jpeg' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
filename: 'test-image.jpg',
|
||||
|
|
@ -125,7 +153,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock text content'], { type: 'text/plain' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
filename: 'test-file.txt',
|
||||
|
|
@ -143,7 +171,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/unknown' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
filename: 'test-file.unknown',
|
||||
|
|
@ -163,7 +191,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const error = {
|
||||
response: { status: 404 }
|
||||
};
|
||||
vi.mocked(api.get).mockRejectedValueOnce(error);
|
||||
mockApiGet.mockRejectedValueOnce(error);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -175,7 +203,7 @@ describe('FailedDocumentViewer', () => {
|
|||
|
||||
test('should show generic error for other failures', async () => {
|
||||
const error = new Error('Network error');
|
||||
vi.mocked(api.get).mockRejectedValueOnce(error);
|
||||
mockApiGet.mockRejectedValueOnce(error);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -189,7 +217,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const error = {
|
||||
response: { status: 500 }
|
||||
};
|
||||
vi.mocked(api.get).mockRejectedValueOnce(error);
|
||||
mockApiGet.mockRejectedValueOnce(error);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -204,7 +232,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -222,34 +250,29 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValue(mockResponse);
|
||||
mockApiGet.mockResolvedValue(mockResponse);
|
||||
|
||||
const { rerender } = renderFailedDocumentViewer();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.get).toHaveBeenCalledWith('/documents/failed/test-failed-doc-id/view', {
|
||||
expect(mockApiGet).toHaveBeenCalledWith('/documents/failed/test-failed-doc-id/view', {
|
||||
responseType: 'blob'
|
||||
});
|
||||
});
|
||||
|
||||
// Change the failedDocumentId
|
||||
const newProps = { ...defaultProps, failedDocumentId: "new-doc-id" };
|
||||
rerender(
|
||||
<ThemeProvider theme={theme}>
|
||||
<FailedDocumentViewer
|
||||
failedDocumentId="new-doc-id"
|
||||
filename="test-document.pdf"
|
||||
mimeType="application/pdf"
|
||||
/>
|
||||
</ThemeProvider>
|
||||
<FailedDocumentViewer {...newProps} />
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.get).toHaveBeenCalledWith('/documents/failed/new-doc-id/view', {
|
||||
expect(mockApiGet).toHaveBeenCalledWith('/documents/failed/new-doc-id/view', {
|
||||
responseType: 'blob'
|
||||
});
|
||||
});
|
||||
|
||||
expect(api.get).toHaveBeenCalledTimes(2);
|
||||
expect(mockApiGet).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -258,7 +281,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock pdf content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
mimeType: 'application/pdf'
|
||||
|
|
@ -278,7 +301,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock image content'], { type: mimeType }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const filename = `test.${mimeType.split('/')[1]}`;
|
||||
renderFailedDocumentViewer({
|
||||
|
|
@ -304,7 +327,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock text content'], { type: mimeType }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const filename = `test.${mimeType.split('/')[1]}`;
|
||||
renderFailedDocumentViewer({
|
||||
|
|
@ -329,7 +352,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -343,7 +366,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock image content'], { type: 'image/jpeg' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
mimeType: 'image/jpeg'
|
||||
|
|
@ -363,12 +386,12 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.get).toHaveBeenCalledWith('/documents/failed/test-failed-doc-id/view', {
|
||||
expect(mockApiGet).toHaveBeenCalledWith('/documents/failed/test-failed-doc-id/view', {
|
||||
responseType: 'blob'
|
||||
});
|
||||
});
|
||||
|
|
@ -378,14 +401,14 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
failedDocumentId: 'different-doc-id'
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.get).toHaveBeenCalledWith('/documents/failed/different-doc-id/view', {
|
||||
expect(mockApiGet).toHaveBeenCalledWith('/documents/failed/different-doc-id/view', {
|
||||
responseType: 'blob'
|
||||
});
|
||||
});
|
||||
|
|
@ -397,7 +420,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob([], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -413,7 +436,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
filename: longFilename
|
||||
|
|
@ -429,7 +452,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
filename: specialFilename
|
||||
|
|
@ -444,7 +467,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: '' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
mimeType: undefined as any
|
||||
|
|
@ -462,7 +485,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock content'], { type: 'application/pdf' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer();
|
||||
|
||||
|
|
@ -476,7 +499,7 @@ describe('FailedDocumentViewer', () => {
|
|||
const mockResponse = {
|
||||
data: new Blob(['mock image content'], { type: 'image/jpeg' }),
|
||||
};
|
||||
vi.mocked(api.get).mockResolvedValueOnce(mockResponse);
|
||||
mockApiGet.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
renderFailedDocumentViewer({
|
||||
mimeType: 'image/jpeg'
|
||||
|
|
|
|||
|
|
@ -1,19 +1,33 @@
|
|||
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { RetryRecommendations } from '../RetryRecommendations';
|
||||
import { createComprehensiveAxiosMock, createComprehensiveApiMocks } from '../../test/comprehensive-mocks';
|
||||
|
||||
// Create unique mock functions for this test file
|
||||
// Mock axios comprehensively to prevent any real HTTP requests
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Create mock functions for this specific test
|
||||
const mockGetRetryRecommendations = vi.fn();
|
||||
const mockBulkRetryOcr = vi.fn();
|
||||
|
||||
// Mock the API module with a unique namespace for this test
|
||||
vi.mock('../../services/api', () => ({
|
||||
documentService: {
|
||||
getRetryRecommendations: mockGetRetryRecommendations,
|
||||
bulkRetryOcr: mockBulkRetryOcr,
|
||||
},
|
||||
}));
|
||||
// Mock the API module with comprehensive mocking
|
||||
vi.mock('../../services/api', async () => {
|
||||
const actual = await vi.importActual('../../services/api');
|
||||
const apiMocks = createComprehensiveApiMocks();
|
||||
|
||||
return {
|
||||
...actual,
|
||||
...apiMocks,
|
||||
documentService: {
|
||||
...apiMocks.documentService,
|
||||
getRetryRecommendations: mockGetRetryRecommendations,
|
||||
bulkRetryOcr: mockBulkRetryOcr,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Import after mocking
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { RetryRecommendations } from '../RetryRecommendations';
|
||||
|
||||
describe('RetryRecommendations', () => {
|
||||
const mockProps = {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { screen, fireEvent } from '@testing-library/react';
|
||||
import { renderWithProviders } from '../../test/test-utils';
|
||||
import TestNotification from '../TestNotification';
|
||||
import { NotificationProvider } from '../../contexts/NotificationContext';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../test/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
const renderTestNotification = () => {
|
||||
|
|
@ -15,7 +15,6 @@ const renderTestNotification = () => {
|
|||
|
||||
describe('TestNotification', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { screen, act } from '@testing-library/react';
|
||||
import { screen, act, render } from '@testing-library/react';
|
||||
import { NotificationProvider, useNotifications } from '../NotificationContext';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../test/test-utils';
|
||||
import { renderWithProviders } from '../../test/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
// Simple test component
|
||||
|
|
@ -62,7 +62,6 @@ const renderWithProvider = () => {
|
|||
|
||||
describe('NotificationContext - Simple Tests', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
|
|
@ -164,7 +163,7 @@ describe('NotificationContext - Simple Tests', () => {
|
|||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
expect(() => {
|
||||
renderWithProviders(<SimpleTestComponent />);
|
||||
render(<SimpleTestComponent />);
|
||||
}).toThrow('useNotifications must be used within NotificationProvider');
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
|
|
@ -198,7 +197,6 @@ describe('NotificationContext - Types', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +1,31 @@
|
|||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { createComprehensiveAxiosMock, createComprehensiveApiMocks } from '../../test/comprehensive-mocks';
|
||||
|
||||
// Mock axios comprehensively to prevent any real HTTP requests
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Mock API services comprehensively
|
||||
vi.mock('../../services/api', async () => {
|
||||
const actual = await vi.importActual('../../services/api');
|
||||
const apiMocks = createComprehensiveApiMocks();
|
||||
|
||||
return {
|
||||
...actual,
|
||||
...apiMocks,
|
||||
};
|
||||
});
|
||||
|
||||
// Import components AFTER the mocks are set up
|
||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import DocumentManagementPage from '../DocumentManagementPage';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import DocumentManagementPage from '../DocumentManagementPage';
|
||||
|
||||
// Create mock objects first
|
||||
const mockDocumentService = {
|
||||
getFailedDocuments: vi.fn(),
|
||||
getFailedOcrDocuments: vi.fn(),
|
||||
getDuplicates: vi.fn(),
|
||||
retryOcr: vi.fn(),
|
||||
deleteLowConfidence: vi.fn(),
|
||||
deleteFailedOcr: vi.fn(),
|
||||
downloadFile: vi.fn(),
|
||||
getRetryRecommendations: vi.fn(),
|
||||
getRetryStats: vi.fn(),
|
||||
getDocumentRetryHistory: vi.fn(),
|
||||
};
|
||||
|
||||
const mockQueueService = {
|
||||
requeueFailed: vi.fn(),
|
||||
};
|
||||
|
||||
const mockApi = {
|
||||
get: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
bulkRetryOcr: vi.fn(),
|
||||
};
|
||||
|
||||
// Mock API with comprehensive responses
|
||||
vi.mock('../../services/api', () => ({
|
||||
api: mockApi,
|
||||
documentService: mockDocumentService,
|
||||
queueService: mockQueueService,
|
||||
}));
|
||||
// Get references to the mocked modules using dynamic import
|
||||
const { api, documentService } = await import('../../services/api');
|
||||
const mockApi = api;
|
||||
const mockDocumentService = documentService;
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
|
|
@ -51,40 +42,11 @@ const DocumentManagementPageWrapper = ({ children }: { children: React.ReactNode
|
|||
describe('DocumentManagementPage - Runtime Error Prevention', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockDocumentService.getFailedDocuments.mockClear();
|
||||
mockDocumentService.getFailedOcrDocuments.mockClear();
|
||||
mockDocumentService.getDuplicates.mockClear();
|
||||
mockQueueService.requeueFailed.mockClear();
|
||||
|
||||
// Setup default mock returns for retry functionality
|
||||
mockDocumentService.getRetryRecommendations.mockResolvedValue({
|
||||
data: { recommendations: [], total_recommendations: 0 }
|
||||
});
|
||||
mockDocumentService.getRetryStats.mockResolvedValue({
|
||||
data: { failure_reasons: [], file_types: [], total_failed: 0 }
|
||||
});
|
||||
mockDocumentService.getDocumentRetryHistory.mockResolvedValue({
|
||||
data: { document_id: 'test', retry_history: [], total_retries: 0 }
|
||||
});
|
||||
mockApi.bulkRetryOcr.mockResolvedValue({
|
||||
data: { success: true, queued_count: 0, matched_count: 0, documents: [] }
|
||||
});
|
||||
});
|
||||
|
||||
describe('OCR Confidence Display - Null Safety', () => {
|
||||
test('basic rendering test', async () => {
|
||||
// Very basic test first - wait for loading to complete
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValue({
|
||||
data: {
|
||||
documents: [],
|
||||
pagination: { total: 0, limit: 25, offset: 0, total_pages: 0 },
|
||||
statistics: { total_failed: 0, by_reason: {}, by_stage: {} },
|
||||
},
|
||||
});
|
||||
|
||||
mockApi.get.mockResolvedValue({
|
||||
data: { total_count: 0, total_size: 0 },
|
||||
});
|
||||
// With axios mocked directly, all API calls should return empty data by default
|
||||
|
||||
render(
|
||||
<DocumentManagementPageWrapper>
|
||||
|
|
@ -559,9 +521,8 @@ describe('DocumentManagementPage - Runtime Error Prevention', () => {
|
|||
|
||||
describe('Ignored Files Tab Functionality', () => {
|
||||
test('should render ignored files tab without errors', async () => {
|
||||
// Mock ignored files API responses
|
||||
const { api } = await import('../../services/api');
|
||||
vi.mocked(api.get).mockImplementation((url) => {
|
||||
// Setup mock responses using our already defined mocks
|
||||
mockApi.get.mockImplementation((url) => {
|
||||
if (url.includes('/ignored-files/stats')) {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
|
|
@ -582,8 +543,7 @@ describe('DocumentManagementPage - Runtime Error Prevention', () => {
|
|||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
|
||||
const { documentService } = await import('../../services/api');
|
||||
vi.mocked(documentService.getFailedDocuments).mockResolvedValueOnce({
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValueOnce({
|
||||
data: {
|
||||
documents: [],
|
||||
pagination: { total: 0, limit: 25, offset: 0, total_pages: 1 },
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { screen, waitFor, act } from '@testing-library/react';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import LabelsPage from '../LabelsPage';
|
||||
import * as useApiModule from '../../hooks/useApi';
|
||||
import { renderWithAuthenticatedUser, setupTestEnvironment } from '../../test/test-utils';\nimport { createMockLabel } from '../../test/label-test-utils';
|
||||
import { renderWithAuthenticatedUser } from '../../test/test-utils';
|
||||
import { createMockLabel } from '../../test/label-test-utils';
|
||||
|
||||
const mockLabels = [
|
||||
createMockLabel({
|
||||
|
|
@ -66,7 +67,6 @@ describe('LabelsPage Component', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
user = userEvent.setup();
|
||||
|
||||
mockApi = {
|
||||
|
|
|
|||
|
|
@ -1,23 +1,26 @@
|
|||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderWithAuthenticatedUser } from '../../test/test-utils';
|
||||
import { createComprehensiveAxiosMock, createComprehensiveApiMocks } from '../../test/comprehensive-mocks';
|
||||
import SearchPage from '../SearchPage';
|
||||
import { renderWithAuthenticatedUser, createMockUser, setupTestEnvironment } from '../../test/test-utils';
|
||||
|
||||
// Mock API functions
|
||||
vi.mock('../../services/api', () => ({
|
||||
searchDocuments: vi.fn(() => Promise.resolve({
|
||||
results: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
page_size: 20
|
||||
})),
|
||||
getSettings: vi.fn(() => Promise.resolve({})),
|
||||
}));
|
||||
// Mock axios comprehensively to prevent any real HTTP requests
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Mock API services comprehensively
|
||||
vi.mock('../../services/api', async () => {
|
||||
const actual = await vi.importActual('../../services/api');
|
||||
const apiMocks = createComprehensiveApiMocks();
|
||||
|
||||
return {
|
||||
...actual,
|
||||
...apiMocks,
|
||||
};
|
||||
});
|
||||
|
||||
describe('SearchPage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setupTestEnvironment();
|
||||
});
|
||||
|
||||
test('renders search page structure', () => {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,26 @@ import React from 'react';
|
|||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import api from '../../services/api';
|
||||
import { createComprehensiveAxiosMock, createComprehensiveApiMocks } from '../../test/comprehensive-mocks';
|
||||
|
||||
// Mock the API
|
||||
vi.mock('../../services/api');
|
||||
const mockedApi = vi.mocked(api);
|
||||
// Mock axios comprehensively to prevent any real HTTP requests
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Mock API services comprehensively
|
||||
vi.mock('../../services/api', async () => {
|
||||
const actual = await vi.importActual('../../services/api');
|
||||
const apiMocks = createComprehensiveApiMocks();
|
||||
|
||||
return {
|
||||
...actual,
|
||||
default: apiMocks.api, // Since this file imports `api` as default
|
||||
...apiMocks,
|
||||
};
|
||||
});
|
||||
|
||||
// Get references to the mocked modules using dynamic import
|
||||
const { default: api } = await import('../../services/api');
|
||||
const mockedApi = api;
|
||||
|
||||
// Mock settings with WebDAV configuration
|
||||
const mockSettings = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { type OcrResponse, type Document } from '../api';
|
||||
import { createMockApiServices, setupTestEnvironment } from '../../test/test-utils';
|
||||
import { createMockApiServices } from '../../test/test-utils';
|
||||
|
||||
// Use centralized API mocking
|
||||
const mockServices = createMockApiServices();
|
||||
|
|
@ -21,7 +21,6 @@ const { documentService } = await import('../api');
|
|||
describe('documentService', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setupTestEnvironment();
|
||||
});
|
||||
|
||||
describe('getOcrText', () => {
|
||||
|
|
@ -124,21 +123,21 @@ describe('documentService', () => {
|
|||
});
|
||||
|
||||
it('should make correct API call', async () => {
|
||||
mockGetOcrText.mockResolvedValue({ data: mockOcrResponse });
|
||||
mockDocumentService.getOcrText.mockResolvedValue({ data: mockOcrResponse });
|
||||
|
||||
await documentService.getOcrText('doc-123');
|
||||
|
||||
expect(mockGetOcrText).toHaveBeenCalledWith('doc-123');
|
||||
expect(mockDocumentService.getOcrText).toHaveBeenCalledWith('doc-123');
|
||||
});
|
||||
|
||||
it('should handle network errors', async () => {
|
||||
mockGetOcrText.mockRejectedValue(new Error('Network Error'));
|
||||
mockDocumentService.getOcrText.mockRejectedValue(new Error('Network Error'));
|
||||
|
||||
await expect(documentService.getOcrText('doc-123')).rejects.toThrow('Network Error');
|
||||
});
|
||||
|
||||
it('should handle 404 errors for non-existent documents', async () => {
|
||||
mockGetOcrText.mockRejectedValue({
|
||||
mockDocumentService.getOcrText.mockRejectedValue({
|
||||
response: {
|
||||
status: 404,
|
||||
data: { error: 'Document not found' },
|
||||
|
|
@ -153,7 +152,7 @@ describe('documentService', () => {
|
|||
});
|
||||
|
||||
it('should handle 401 unauthorized errors', async () => {
|
||||
mockGetOcrText.mockRejectedValue({
|
||||
mockDocumentService.getOcrText.mockRejectedValue({
|
||||
response: {
|
||||
status: 401,
|
||||
data: { error: 'Unauthorized' },
|
||||
|
|
@ -209,7 +208,7 @@ describe('documentService', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockList.mockResolvedValue(mockResponse);
|
||||
mockDocumentService.list.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await documentService.list(50, 0);
|
||||
|
||||
|
|
@ -236,24 +235,24 @@ describe('documentService', () => {
|
|||
ocr_status: 'pending',
|
||||
};
|
||||
|
||||
mockUpload.mockResolvedValue({ data: mockUploadResponse });
|
||||
mockDocumentService.upload.mockResolvedValue({ data: mockUploadResponse });
|
||||
|
||||
const result = await documentService.upload(mockFile);
|
||||
|
||||
expect(result.data).toEqual(mockUploadResponse);
|
||||
expect(mockUpload).toHaveBeenCalledWith(mockFile);
|
||||
expect(mockDocumentService.upload).toHaveBeenCalledWith(mockFile);
|
||||
});
|
||||
});
|
||||
|
||||
describe('download', () => {
|
||||
it('should download file as blob', async () => {
|
||||
const mockBlob = new Blob(['file content'], { type: 'application/pdf' });
|
||||
mockDownload.mockResolvedValue({ data: mockBlob });
|
||||
mockDocumentService.download.mockResolvedValue({ data: mockBlob });
|
||||
|
||||
const result = await documentService.download('doc-123');
|
||||
|
||||
expect(result.data).toEqual(mockBlob);
|
||||
expect(mockDownload).toHaveBeenCalledWith('doc-123');
|
||||
expect(mockDocumentService.download).toHaveBeenCalledWith('doc-123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -325,11 +324,11 @@ describe('documentService.deleteLowConfidence', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockDeleteLowConfidence.mockResolvedValue(mockDeleteResponse);
|
||||
mockDocumentService.deleteLowConfidence.mockResolvedValue(mockDeleteResponse);
|
||||
|
||||
const result = await documentService.deleteLowConfidence(30.0, false);
|
||||
|
||||
expect(mockDeleteLowConfidence).toHaveBeenCalledWith(30.0, false);
|
||||
expect(mockDocumentService.deleteLowConfidence).toHaveBeenCalledWith(30.0, false);
|
||||
expect(result.data.success).toBe(true);
|
||||
expect(result.data.deleted_count).toBe(3);
|
||||
expect(result.data.matched_count).toBe(3);
|
||||
|
|
@ -351,11 +350,11 @@ describe('documentService.deleteLowConfidence', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockDeleteLowConfidence.mockResolvedValue(mockPreviewResponse);
|
||||
mockDocumentService.deleteLowConfidence.mockResolvedValue(mockPreviewResponse);
|
||||
|
||||
const result = await documentService.deleteLowConfidence(50.0, true);
|
||||
|
||||
expect(mockDeleteLowConfidence).toHaveBeenCalledWith(50.0, true);
|
||||
expect(mockDocumentService.deleteLowConfidence).toHaveBeenCalledWith(50.0, true);
|
||||
expect(result.data.success).toBe(true);
|
||||
expect(result.data.preview).toBe(true);
|
||||
expect(result.data.matched_count).toBe(5);
|
||||
|
|
@ -376,11 +375,11 @@ describe('documentService.deleteLowConfidence', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockDeleteLowConfidence.mockResolvedValue(mockEmptyResponse);
|
||||
mockDocumentService.deleteLowConfidence.mockResolvedValue(mockEmptyResponse);
|
||||
|
||||
const result = await documentService.deleteLowConfidence(10.0, false);
|
||||
|
||||
expect(mockDeleteLowConfidence).toHaveBeenCalledWith(10.0, false);
|
||||
expect(mockDocumentService.deleteLowConfidence).toHaveBeenCalledWith(10.0, false);
|
||||
expect(result.data.success).toBe(true);
|
||||
expect(result.data.deleted_count).toBe(0);
|
||||
});
|
||||
|
|
@ -398,23 +397,23 @@ describe('documentService.deleteLowConfidence', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockDeleteLowConfidence.mockResolvedValue(mockErrorResponse);
|
||||
mockDocumentService.deleteLowConfidence.mockResolvedValue(mockErrorResponse);
|
||||
|
||||
const result = await documentService.deleteLowConfidence(-10.0, false);
|
||||
|
||||
expect(mockDeleteLowConfidence).toHaveBeenCalledWith(-10.0, false);
|
||||
expect(mockDocumentService.deleteLowConfidence).toHaveBeenCalledWith(-10.0, false);
|
||||
expect(result.data.success).toBe(false);
|
||||
expect(result.data.message).toContain('must be between 0.0 and 100.0');
|
||||
});
|
||||
|
||||
it('should handle API errors gracefully', async () => {
|
||||
const mockError = new Error('Network error');
|
||||
mockDeleteLowConfidence.mockRejectedValue(mockError);
|
||||
mockDocumentService.deleteLowConfidence.mockRejectedValue(mockError);
|
||||
|
||||
await expect(documentService.deleteLowConfidence(30.0, false))
|
||||
.rejects.toThrow('Network error');
|
||||
|
||||
expect(mockDeleteLowConfidence).toHaveBeenCalledWith(30.0, false);
|
||||
expect(mockDocumentService.deleteLowConfidence).toHaveBeenCalledWith(30.0, false);
|
||||
});
|
||||
|
||||
it('should use correct default values', async () => {
|
||||
|
|
@ -426,12 +425,12 @@ describe('documentService.deleteLowConfidence', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockDeleteLowConfidence.mockResolvedValue(mockResponse);
|
||||
mockDocumentService.deleteLowConfidence.mockResolvedValue(mockResponse);
|
||||
|
||||
// Test with explicit false value (the default)
|
||||
await documentService.deleteLowConfidence(40.0, false);
|
||||
|
||||
expect(mockDeleteLowConfidence).toHaveBeenCalledWith(40.0, false);
|
||||
expect(mockDocumentService.deleteLowConfidence).toHaveBeenCalledWith(40.0, false);
|
||||
});
|
||||
|
||||
it('should handle partial deletion failures', async () => {
|
||||
|
|
@ -452,7 +451,7 @@ describe('documentService.deleteLowConfidence', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockDeleteLowConfidence.mockResolvedValue(mockPartialFailureResponse);
|
||||
mockDocumentService.deleteLowConfidence.mockResolvedValue(mockPartialFailureResponse);
|
||||
|
||||
const result = await documentService.deleteLowConfidence(25.0, false);
|
||||
|
||||
|
|
@ -472,15 +471,15 @@ describe('documentService.deleteLowConfidence', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockDeleteLowConfidence.mockResolvedValue(mockResponse);
|
||||
mockDocumentService.deleteLowConfidence.mockResolvedValue(mockResponse);
|
||||
|
||||
// Test various confidence values
|
||||
const testValues = [0.0, 0.1, 30.5, 50.0, 99.9, 100.0];
|
||||
|
||||
for (const confidence of testValues) {
|
||||
mockDeleteLowConfidence.mockClear();
|
||||
mockDocumentService.deleteLowConfidence.mockClear();
|
||||
await documentService.deleteLowConfidence(confidence, true);
|
||||
expect(mockDeleteLowConfidence).toHaveBeenCalledWith(confidence, true);
|
||||
expect(mockDocumentService.deleteLowConfidence).toHaveBeenCalledWith(confidence, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -529,11 +528,11 @@ describe('documentService.getFailedOcrDocuments', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockGetFailedOcrDocuments.mockResolvedValue(mockResponse);
|
||||
mockDocumentService.getFailedOcrDocuments.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await documentService.getFailedOcrDocuments(50, 0);
|
||||
|
||||
expect(mockGetFailedOcrDocuments).toHaveBeenCalledWith(50, 0);
|
||||
expect(mockDocumentService.getFailedOcrDocuments).toHaveBeenCalledWith(50, 0);
|
||||
expect(result.data).toEqual(mockFailedOcrResponse);
|
||||
expect(result.data.documents).toHaveLength(2);
|
||||
expect(result.data.documents[0].failure_stage).toBe('ocr');
|
||||
|
|
@ -541,19 +540,19 @@ describe('documentService.getFailedOcrDocuments', () => {
|
|||
});
|
||||
|
||||
it('should handle pagination parameters correctly', async () => {
|
||||
mockGetFailedOcrDocuments.mockResolvedValue({ data: mockFailedOcrResponse });
|
||||
mockDocumentService.getFailedOcrDocuments.mockResolvedValue({ data: mockFailedOcrResponse });
|
||||
|
||||
await documentService.getFailedOcrDocuments(25, 10);
|
||||
|
||||
expect(mockGetFailedOcrDocuments).toHaveBeenCalledWith(25, 10);
|
||||
expect(mockDocumentService.getFailedOcrDocuments).toHaveBeenCalledWith(25, 10);
|
||||
});
|
||||
|
||||
it('should use default pagination when not specified', async () => {
|
||||
mockGetFailedOcrDocuments.mockResolvedValue({ data: mockFailedOcrResponse });
|
||||
mockDocumentService.getFailedOcrDocuments.mockResolvedValue({ data: mockFailedOcrResponse });
|
||||
|
||||
await documentService.getFailedOcrDocuments();
|
||||
|
||||
expect(mockGetFailedOcrDocuments).toHaveBeenCalledWith();
|
||||
expect(mockDocumentService.getFailedOcrDocuments).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should handle empty results', async () => {
|
||||
|
|
@ -563,7 +562,7 @@ describe('documentService.getFailedOcrDocuments', () => {
|
|||
statistics: { total_failed: 0, failure_categories: [] }
|
||||
};
|
||||
|
||||
mockGetFailedOcrDocuments.mockResolvedValue({ data: emptyResponse });
|
||||
mockDocumentService.getFailedOcrDocuments.mockResolvedValue({ data: emptyResponse });
|
||||
|
||||
const result = await documentService.getFailedOcrDocuments();
|
||||
|
||||
|
|
@ -574,7 +573,7 @@ describe('documentService.getFailedOcrDocuments', () => {
|
|||
|
||||
it('should handle API errors', async () => {
|
||||
const mockError = new Error('Network error');
|
||||
mockGetFailedOcrDocuments.mockRejectedValue(mockError);
|
||||
mockDocumentService.getFailedOcrDocuments.mockRejectedValue(mockError);
|
||||
|
||||
await expect(documentService.getFailedOcrDocuments()).rejects.toThrow('Network error');
|
||||
});
|
||||
|
|
@ -625,11 +624,11 @@ describe('documentService.getFailedDocuments', () => {
|
|||
};
|
||||
|
||||
it('should fetch failed documents with default parameters', async () => {
|
||||
mockGetFailedDocuments.mockResolvedValue({ data: mockFailedDocumentsResponse });
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValue({ data: mockFailedDocumentsResponse });
|
||||
|
||||
const result = await documentService.getFailedDocuments();
|
||||
|
||||
expect(mockGetFailedDocuments).toHaveBeenCalledWith();
|
||||
expect(mockDocumentService.getFailedDocuments).toHaveBeenCalledWith();
|
||||
expect(result.data).toEqual(mockFailedDocumentsResponse);
|
||||
expect(result.data.documents).toHaveLength(3);
|
||||
});
|
||||
|
|
@ -642,11 +641,11 @@ describe('documentService.getFailedDocuments', () => {
|
|||
statistics: { total_failed: 1, failure_categories: [{ reason: 'low_ocr_confidence', display_name: 'Low OCR Confidence', count: 1 }] }
|
||||
};
|
||||
|
||||
mockGetFailedDocuments.mockResolvedValue({ data: ocrOnlyResponse });
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValue({ data: ocrOnlyResponse });
|
||||
|
||||
const result = await documentService.getFailedDocuments(25, 0, 'ocr');
|
||||
|
||||
expect(mockGetFailedDocuments).toHaveBeenCalledWith(25, 0, 'ocr');
|
||||
expect(mockDocumentService.getFailedDocuments).toHaveBeenCalledWith(25, 0, 'ocr');
|
||||
expect(result.data.documents).toHaveLength(1);
|
||||
expect(result.data.documents[0].failure_stage).toBe('ocr');
|
||||
});
|
||||
|
|
@ -659,11 +658,11 @@ describe('documentService.getFailedDocuments', () => {
|
|||
statistics: { total_failed: 1, failure_categories: [{ reason: 'duplicate_content', display_name: 'Duplicate Content', count: 1 }] }
|
||||
};
|
||||
|
||||
mockGetFailedDocuments.mockResolvedValue({ data: duplicateOnlyResponse });
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValue({ data: duplicateOnlyResponse });
|
||||
|
||||
const result = await documentService.getFailedDocuments(25, 0, undefined, 'duplicate_content');
|
||||
|
||||
expect(mockGetFailedDocuments).toHaveBeenCalledWith(25, 0, undefined, 'duplicate_content');
|
||||
expect(mockDocumentService.getFailedDocuments).toHaveBeenCalledWith(25, 0, undefined, 'duplicate_content');
|
||||
expect(result.data.documents).toHaveLength(1);
|
||||
expect(result.data.documents[0].failure_reason).toBe('duplicate_content');
|
||||
});
|
||||
|
|
@ -676,22 +675,22 @@ describe('documentService.getFailedDocuments', () => {
|
|||
statistics: { total_failed: 1, failure_categories: [{ reason: 'low_ocr_confidence', display_name: 'Low OCR Confidence', count: 1 }] }
|
||||
};
|
||||
|
||||
mockGetFailedDocuments.mockResolvedValue({ data: filteredResponse });
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValue({ data: filteredResponse });
|
||||
|
||||
const result = await documentService.getFailedDocuments(25, 0, 'ocr', 'low_ocr_confidence');
|
||||
|
||||
expect(mockGetFailedDocuments).toHaveBeenCalledWith(25, 0, 'ocr', 'low_ocr_confidence');
|
||||
expect(mockDocumentService.getFailedDocuments).toHaveBeenCalledWith(25, 0, 'ocr', 'low_ocr_confidence');
|
||||
expect(result.data.documents).toHaveLength(1);
|
||||
expect(result.data.documents[0].failure_stage).toBe('ocr');
|
||||
expect(result.data.documents[0].failure_reason).toBe('low_ocr_confidence');
|
||||
});
|
||||
|
||||
it('should handle custom pagination', async () => {
|
||||
mockGetFailedDocuments.mockResolvedValue({ data: mockFailedDocumentsResponse });
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValue({ data: mockFailedDocumentsResponse });
|
||||
|
||||
await documentService.getFailedDocuments(10, 20);
|
||||
|
||||
expect(mockGetFailedDocuments).toHaveBeenCalledWith(10, 20);
|
||||
expect(mockDocumentService.getFailedDocuments).toHaveBeenCalledWith(10, 20);
|
||||
});
|
||||
|
||||
it('should handle empty results', async () => {
|
||||
|
|
@ -701,7 +700,7 @@ describe('documentService.getFailedDocuments', () => {
|
|||
statistics: { total_failed: 0, failure_categories: [] }
|
||||
};
|
||||
|
||||
mockGetFailedDocuments.mockResolvedValue({ data: emptyResponse });
|
||||
mockDocumentService.getFailedDocuments.mockResolvedValue({ data: emptyResponse });
|
||||
|
||||
const result = await documentService.getFailedDocuments();
|
||||
|
||||
|
|
@ -724,11 +723,11 @@ describe('documentService.retryOcr', () => {
|
|||
config: {},
|
||||
};
|
||||
|
||||
mockRetryOcr.mockResolvedValue(mockRetryResponse);
|
||||
mockDocumentService.retryOcr.mockResolvedValue(mockRetryResponse);
|
||||
|
||||
const result = await documentService.retryOcr('doc-123');
|
||||
|
||||
expect(mockRetryOcr).toHaveBeenCalledWith('doc-123');
|
||||
expect(mockDocumentService.retryOcr).toHaveBeenCalledWith('doc-123');
|
||||
expect(result.data.success).toBe(true);
|
||||
expect(result.data.document_id).toBe('doc-123');
|
||||
});
|
||||
|
|
@ -741,7 +740,7 @@ describe('documentService.retryOcr', () => {
|
|||
}
|
||||
};
|
||||
|
||||
mockRetryOcr.mockRejectedValue(mockError);
|
||||
mockDocumentService.retryOcr.mockRejectedValue(mockError);
|
||||
|
||||
await expect(documentService.retryOcr('non-existent-doc')).rejects.toMatchObject({
|
||||
response: { status: 404 }
|
||||
|
|
@ -749,7 +748,7 @@ describe('documentService.retryOcr', () => {
|
|||
});
|
||||
|
||||
it('should handle network errors', async () => {
|
||||
mockRetryOcr.mockRejectedValue(new Error('Network error'));
|
||||
mockDocumentService.retryOcr.mockRejectedValue(new Error('Network error'));
|
||||
|
||||
await expect(documentService.retryOcr('doc-123')).rejects.toThrow('Network error');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
// Comprehensive mocking utilities to prevent HTTP requests in tests
|
||||
import { vi } from 'vitest';
|
||||
|
||||
/**
|
||||
* Creates a comprehensive axios mock that prevents all HTTP requests
|
||||
* This should be used in test files that have components making API calls
|
||||
*/
|
||||
export const createComprehensiveAxiosMock = () => {
|
||||
const mockAxiosInstance = {
|
||||
get: vi.fn().mockResolvedValue({ data: {} }),
|
||||
post: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
put: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
delete: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
patch: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
request: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
head: vi.fn().mockResolvedValue({ data: {} }),
|
||||
options: vi.fn().mockResolvedValue({ data: {} }),
|
||||
defaults: {
|
||||
headers: {
|
||||
common: {},
|
||||
get: {},
|
||||
post: {},
|
||||
put: {},
|
||||
delete: {},
|
||||
patch: {},
|
||||
}
|
||||
},
|
||||
interceptors: {
|
||||
request: { use: vi.fn(), eject: vi.fn() },
|
||||
response: { use: vi.fn(), eject: vi.fn() },
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
default: {
|
||||
create: vi.fn(() => mockAxiosInstance),
|
||||
...mockAxiosInstance,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates comprehensive API service mocks
|
||||
*/
|
||||
export const createComprehensiveApiMocks = () => ({
|
||||
api: {
|
||||
get: vi.fn().mockResolvedValue({ data: {} }),
|
||||
post: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
put: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
delete: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
patch: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
defaults: { headers: { common: {} } },
|
||||
},
|
||||
documentService: {
|
||||
getRetryRecommendations: vi.fn().mockResolvedValue({
|
||||
data: { recommendations: [], total_recommendations: 0 }
|
||||
}),
|
||||
bulkRetryOcr: vi.fn().mockResolvedValue({ data: { success: true } }),
|
||||
getFailedDocuments: vi.fn().mockResolvedValue({
|
||||
data: {
|
||||
documents: [],
|
||||
pagination: { total: 0, limit: 25, offset: 0, total_pages: 1 },
|
||||
statistics: { total_failed: 0, by_reason: {}, by_stage: {} },
|
||||
},
|
||||
}),
|
||||
getDuplicates: vi.fn().mockResolvedValue({
|
||||
data: {
|
||||
duplicates: [],
|
||||
pagination: { total: 0, limit: 25, offset: 0, has_more: false },
|
||||
statistics: { total_duplicate_groups: 0 },
|
||||
},
|
||||
}),
|
||||
enhancedSearch: vi.fn().mockResolvedValue({
|
||||
data: {
|
||||
documents: [],
|
||||
total: 0,
|
||||
query_time_ms: 0,
|
||||
suggestions: []
|
||||
}
|
||||
}),
|
||||
search: vi.fn().mockResolvedValue({
|
||||
data: {
|
||||
documents: [],
|
||||
total: 0,
|
||||
query_time_ms: 0,
|
||||
suggestions: []
|
||||
}
|
||||
}),
|
||||
getOcrText: vi.fn().mockResolvedValue({ data: {} }),
|
||||
upload: vi.fn().mockResolvedValue({ data: {} }),
|
||||
list: vi.fn().mockResolvedValue({ data: [] }),
|
||||
listWithPagination: vi.fn().mockResolvedValue({
|
||||
data: {
|
||||
documents: [],
|
||||
pagination: { total: 0, limit: 20, offset: 0, has_more: false }
|
||||
}
|
||||
}),
|
||||
delete: vi.fn().mockResolvedValue({}),
|
||||
bulkDelete: vi.fn().mockResolvedValue({}),
|
||||
retryOcr: vi.fn().mockResolvedValue({}),
|
||||
getFacets: vi.fn().mockResolvedValue({ data: { mime_types: [], tags: [] } }),
|
||||
download: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
deleteLowConfidence: vi.fn().mockResolvedValue({ data: {} }),
|
||||
deleteFailedOcr: vi.fn().mockResolvedValue({ data: {} }),
|
||||
view: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
getThumbnail: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
getProcessedImage: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
downloadFile: vi.fn().mockResolvedValue(undefined),
|
||||
getRetryStats: vi.fn().mockResolvedValue({ data: {} }),
|
||||
getDocumentRetryHistory: vi.fn().mockResolvedValue({ data: [] }),
|
||||
},
|
||||
queueService: {
|
||||
getQueueStatus: vi.fn().mockResolvedValue({ data: { active: 0, waiting: 0 } }),
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Standard pattern for mocking both axios and API services
|
||||
* Use this at the top of test files that have components making API calls
|
||||
*/
|
||||
export const setupHttpMocks = () => {
|
||||
// Mock axios comprehensively
|
||||
vi.mock('axios', () => createComprehensiveAxiosMock());
|
||||
|
||||
// Mock API services
|
||||
const apiMocks = createComprehensiveApiMocks();
|
||||
|
||||
return apiMocks;
|
||||
};
|
||||
|
|
@ -5,8 +5,11 @@ import { type LabelData } from '../components/Labels/Label';
|
|||
* Test utilities for label-related tests
|
||||
*/
|
||||
|
||||
// Counter for generating unique IDs
|
||||
let labelIdCounter = 1;
|
||||
|
||||
export const createMockLabel = (overrides: Partial<LabelData> = {}): LabelData => ({
|
||||
id: 'test-label-1',
|
||||
id: `test-label-${labelIdCounter++}`,
|
||||
name: 'Test Label',
|
||||
description: 'A test label',
|
||||
color: '#0969da',
|
||||
|
|
@ -21,12 +24,15 @@ export const createMockLabel = (overrides: Partial<LabelData> = {}): LabelData =
|
|||
});
|
||||
|
||||
export const createMockSystemLabel = (overrides: Partial<LabelData> = {}): LabelData => ({
|
||||
...createMockLabel(),
|
||||
id: 'system-label-1',
|
||||
id: `system-label-${labelIdCounter++}`,
|
||||
name: 'Important',
|
||||
description: 'A test label',
|
||||
color: '#d73a49',
|
||||
background_color: undefined,
|
||||
icon: 'star',
|
||||
is_system: true,
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
updated_at: '2024-01-01T00:00:00Z',
|
||||
document_count: 10,
|
||||
source_count: 2,
|
||||
...overrides,
|
||||
|
|
@ -213,12 +219,12 @@ export const testDataBuilders = {
|
|||
* Creates a set of labels that represent typical usage patterns
|
||||
*/
|
||||
createTypicalLabelSet: (): LabelData[] => [
|
||||
createMockSystemLabel({ name: 'Important', color: '#d73a49', icon: 'star' }),
|
||||
createMockSystemLabel({ name: 'Work', color: '#0969da', icon: 'work' }),
|
||||
createMockSystemLabel({ name: 'Personal', color: '#28a745', icon: 'person' }),
|
||||
createMockLabel({ name: 'Project Alpha', color: '#8250df', icon: 'folder' }),
|
||||
createMockLabel({ name: 'Invoices', color: '#fb8500', icon: 'receipt' }),
|
||||
createMockLabel({ name: 'Archive', color: '#6e7781', icon: 'archive', document_count: 0 }),
|
||||
createMockSystemLabel({ name: 'Important', description: 'High priority items', color: '#d73a49', icon: 'star' }),
|
||||
createMockSystemLabel({ name: 'Work', description: 'Work-related documents', color: '#0969da', icon: 'work' }),
|
||||
createMockSystemLabel({ name: 'Personal', description: 'Personal documents', color: '#28a745', icon: 'person' }),
|
||||
createMockLabel({ name: 'Project Alpha', description: 'My personal project files', color: '#8250df', icon: 'folder' }),
|
||||
createMockLabel({ name: 'Invoices', description: 'Financial documents', color: '#fb8500', icon: 'receipt' }),
|
||||
createMockLabel({ name: 'Archive', description: 'Archived items', color: '#6e7781', icon: 'archive', document_count: 0 }),
|
||||
],
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import '@testing-library/jest-dom'
|
||||
import { vi } from 'vitest'
|
||||
import { setupTestEnvironment } from './test-utils'
|
||||
import { setupTestEnvironment } from './test-utils.tsx'
|
||||
|
||||
// Setup global test environment
|
||||
setupTestEnvironment()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import { render, RenderOptions } from '@testing-library/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { vi } from 'vitest'
|
||||
import { NotificationProvider } from '../contexts/NotificationContext'
|
||||
|
||||
interface User {
|
||||
id: string
|
||||
|
|
@ -38,12 +39,46 @@ export const createMockAdminUser = (overrides: Partial<User> = {}): User => ({
|
|||
// Centralized API mocking to eliminate per-file duplication
|
||||
export const createMockApiServices = () => {
|
||||
const mockDocumentService = {
|
||||
enhancedSearch: vi.fn().mockResolvedValue({ documents: [], total: 0 }),
|
||||
enhancedSearch: vi.fn().mockResolvedValue({
|
||||
data: {
|
||||
documents: [],
|
||||
total: 0,
|
||||
query_time_ms: 0,
|
||||
suggestions: []
|
||||
}
|
||||
}),
|
||||
search: vi.fn().mockResolvedValue({
|
||||
data: {
|
||||
documents: [],
|
||||
total: 0,
|
||||
query_time_ms: 0,
|
||||
suggestions: []
|
||||
}
|
||||
}),
|
||||
bulkRetryOcr: vi.fn().mockResolvedValue({ success: true }),
|
||||
getDocument: vi.fn().mockResolvedValue({}),
|
||||
uploadDocument: vi.fn().mockResolvedValue({}),
|
||||
deleteDocument: vi.fn().mockResolvedValue({}),
|
||||
updateDocument: vi.fn().mockResolvedValue({}),
|
||||
getById: vi.fn().mockResolvedValue({ data: {} }),
|
||||
upload: vi.fn().mockResolvedValue({ data: {} }),
|
||||
list: vi.fn().mockResolvedValue({ data: [] }),
|
||||
listWithPagination: vi.fn().mockResolvedValue({ data: { documents: [], pagination: { total: 0, limit: 20, offset: 0, has_more: false } } }),
|
||||
delete: vi.fn().mockResolvedValue({}),
|
||||
bulkDelete: vi.fn().mockResolvedValue({}),
|
||||
retryOcr: vi.fn().mockResolvedValue({}),
|
||||
getFacets: vi.fn().mockResolvedValue({ data: { mime_types: [], tags: [] } }),
|
||||
getOcrText: vi.fn().mockResolvedValue({ data: {} }),
|
||||
download: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
getFailedOcrDocuments: vi.fn().mockResolvedValue({ data: [] }),
|
||||
getFailedDocuments: vi.fn().mockResolvedValue({ data: [] }),
|
||||
deleteLowConfidence: vi.fn().mockResolvedValue({ data: {} }),
|
||||
deleteFailedOcr: vi.fn().mockResolvedValue({ data: {} }),
|
||||
view: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
getThumbnail: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
getProcessedImage: vi.fn().mockResolvedValue({ data: new Blob() }),
|
||||
downloadFile: vi.fn().mockResolvedValue(undefined),
|
||||
getDuplicates: vi.fn().mockResolvedValue({ data: [] }),
|
||||
getRetryStats: vi.fn().mockResolvedValue({ data: {} }),
|
||||
getRetryRecommendations: vi.fn().mockResolvedValue({ data: [] }),
|
||||
getDocumentRetryHistory: vi.fn().mockResolvedValue({ data: [] }),
|
||||
}
|
||||
|
||||
const mockAuthService = {
|
||||
|
|
@ -77,24 +112,11 @@ export const createMockApiServices = () => {
|
|||
}
|
||||
|
||||
// Setup global API mocks (call this in setup files)
|
||||
// Note: Individual test files should handle their own vi.mock() calls
|
||||
export const setupApiMocks = () => {
|
||||
const mockServices = createMockApiServices()
|
||||
|
||||
vi.mock('../../services/api', () => ({
|
||||
documentService: mockServices.documentService,
|
||||
authService: mockServices.authService,
|
||||
sourceService: mockServices.sourceService,
|
||||
labelService: mockServices.labelService,
|
||||
api: {
|
||||
defaults: {
|
||||
headers: {
|
||||
common: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
return mockServices
|
||||
// Just return the mock services for use in tests
|
||||
// The actual vi.mock() should be done in individual test files
|
||||
return createMockApiServices()
|
||||
}
|
||||
|
||||
// Create a mock AuthProvider for testing
|
||||
|
|
@ -136,9 +158,11 @@ const AllTheProviders = ({
|
|||
}) => {
|
||||
return (
|
||||
<BrowserRouter {...routerProps}>
|
||||
<MockAuthProvider mockValues={authValues}>
|
||||
{children}
|
||||
</MockAuthProvider>
|
||||
<NotificationProvider>
|
||||
<MockAuthProvider mockValues={authValues}>
|
||||
{children}
|
||||
</MockAuthProvider>
|
||||
</NotificationProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue