unit test auto-provider refactor
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 19m58s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 19m58s
This commit is contained in:
@@ -4,13 +4,14 @@ import { screen, waitFor } from '@testing-library/react';
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { AppGuard } from './AppGuard';
|
import { AppGuard } from './AppGuard';
|
||||||
import { useAppInitialization } from '../hooks/useAppInitialization';
|
import { useAppInitialization } from '../hooks/useAppInitialization';
|
||||||
|
import * as apiClient from '../services/apiClient';
|
||||||
import { useModal } from '../hooks/useModal';
|
import { useModal } from '../hooks/useModal';
|
||||||
import { renderWithProviders } from '../tests/utils/renderWithProviders';
|
import { renderWithProviders } from '../tests/utils/renderWithProviders';
|
||||||
|
|
||||||
// Mock dependencies
|
// Mock dependencies
|
||||||
|
// The apiClient is mocked globally in `src/tests/setup/globalApiMock.ts`.
|
||||||
vi.mock('../hooks/useAppInitialization');
|
vi.mock('../hooks/useAppInitialization');
|
||||||
vi.mock('../hooks/useModal');
|
vi.mock('../hooks/useModal');
|
||||||
vi.mock('../services/apiClient');
|
|
||||||
vi.mock('./WhatsNewModal', () => ({
|
vi.mock('./WhatsNewModal', () => ({
|
||||||
WhatsNewModal: ({ isOpen }: { isOpen: boolean }) =>
|
WhatsNewModal: ({ isOpen }: { isOpen: boolean }) =>
|
||||||
isOpen ? <div data-testid="whats-new-modal-mock" /> : null,
|
isOpen ? <div data-testid="whats-new-modal-mock" /> : null,
|
||||||
@@ -21,6 +22,7 @@ vi.mock('../config', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
const mockedUseAppInitialization = vi.mocked(useAppInitialization);
|
const mockedUseAppInitialization = vi.mocked(useAppInitialization);
|
||||||
const mockedUseModal = vi.mocked(useModal);
|
const mockedUseModal = vi.mocked(useModal);
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,9 @@ import { renderWithProviders } from '../tests/utils/renderWithProviders';
|
|||||||
// Unmock the component to test the real implementation
|
// Unmock the component to test the real implementation
|
||||||
vi.unmock('./FlyerCorrectionTool');
|
vi.unmock('./FlyerCorrectionTool');
|
||||||
|
|
||||||
// Mock dependencies
|
// The aiApiClient, notificationService, and logger are mocked globally.
|
||||||
vi.mock('../services/aiApiClient');
|
// We can get a typed reference to the aiApiClient for individual test overrides.
|
||||||
vi.mock('../services/notificationService');
|
const mockedAiApiClient = vi.mocked(aiApiClient);
|
||||||
vi.mock('../services/logger', () => ({
|
|
||||||
logger: {
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const mockedAiApiClient = aiApiClient as Mocked<typeof aiApiClient>;
|
|
||||||
const mockedNotifySuccess = notifySuccess as Mocked<typeof notifySuccess>;
|
const mockedNotifySuccess = notifySuccess as Mocked<typeof notifySuccess>;
|
||||||
const mockedNotifyError = notifyError as Mocked<typeof notifyError>;
|
const mockedNotifyError = notifyError as Mocked<typeof notifyError>;
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,9 @@ import { createMockLeaderboardUser } from '../tests/utils/mockFactories';
|
|||||||
import { createMockLogger } from '../tests/utils/mockLogger';
|
import { createMockLogger } from '../tests/utils/mockLogger';
|
||||||
import { renderWithProviders } from '../tests/utils/renderWithProviders';
|
import { renderWithProviders } from '../tests/utils/renderWithProviders';
|
||||||
|
|
||||||
// Mock the apiClient
|
// The apiClient and logger are mocked globally.
|
||||||
vi.mock('../services/apiClient'); // This was correct
|
// We can get a typed reference to the apiClient for individual test overrides.
|
||||||
const mockedApiClient = apiClient as Mocked<typeof apiClient>;
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|
||||||
// Mock the logger
|
|
||||||
vi.mock('../services/logger', () => ({
|
|
||||||
logger: createMockLogger(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock lucide-react icons to prevent rendering errors in the test environment
|
// Mock lucide-react icons to prevent rendering errors in the test environment
|
||||||
vi.mock('lucide-react', () => ({
|
vi.mock('lucide-react', () => ({
|
||||||
|
|||||||
@@ -2,16 +2,15 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { screen, waitFor } from '@testing-library/react';
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { RecipeSuggester } from './RecipeSuggester';
|
import { RecipeSuggester } from './RecipeSuggester'; // This should be after mocks
|
||||||
import { suggestRecipe } from '../services/apiClient';
|
import * as apiClient from '../services/apiClient';
|
||||||
import { logger } from '../services/logger.client';
|
import { logger } from '../services/logger.client';
|
||||||
import { renderWithProviders } from '../tests/utils/renderWithProviders';
|
import { renderWithProviders } from '../tests/utils/renderWithProviders';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
|
||||||
// Mock the API client
|
// The apiClient is mocked globally in `src/tests/setup/globalApiMock.ts`.
|
||||||
vi.mock('../services/apiClient', () => ({
|
// We can get a typed reference to it for individual test overrides.
|
||||||
suggestRecipe: vi.fn(),
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock the logger
|
// Mock the logger
|
||||||
vi.mock('../services/logger.client', () => ({
|
vi.mock('../services/logger.client', () => ({
|
||||||
@@ -45,7 +44,7 @@ describe('RecipeSuggester Component', () => {
|
|||||||
await user.click(button);
|
await user.click(button);
|
||||||
|
|
||||||
expect(await screen.findByText('Please enter at least one ingredient.')).toBeInTheDocument();
|
expect(await screen.findByText('Please enter at least one ingredient.')).toBeInTheDocument();
|
||||||
expect(suggestRecipe).not.toHaveBeenCalled();
|
expect(mockedApiClient.suggestRecipe).not.toHaveBeenCalled();
|
||||||
console.log('TEST: Validation error displayed correctly');
|
console.log('TEST: Validation error displayed correctly');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ describe('RecipeSuggester Component', () => {
|
|||||||
// Mock successful API response
|
// Mock successful API response
|
||||||
const mockSuggestion = 'Here is a nice Chicken and Rice recipe...';
|
const mockSuggestion = 'Here is a nice Chicken and Rice recipe...';
|
||||||
// Add a delay to ensure the loading state is visible during the test
|
// Add a delay to ensure the loading state is visible during the test
|
||||||
vi.mocked(suggestRecipe).mockImplementation(async () => {
|
mockedApiClient.suggestRecipe.mockImplementation(async () => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||||
return { ok: true, json: async () => ({ suggestion: mockSuggestion }) } as Response;
|
return { ok: true, json: async () => ({ suggestion: mockSuggestion }) } as Response;
|
||||||
});
|
});
|
||||||
@@ -76,7 +75,7 @@ describe('RecipeSuggester Component', () => {
|
|||||||
expect(screen.getByText(mockSuggestion)).toBeInTheDocument();
|
expect(screen.getByText(mockSuggestion)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(suggestRecipe).toHaveBeenCalledWith(['chicken', 'rice']);
|
expect(mockedApiClient.suggestRecipe).toHaveBeenCalledWith(['chicken', 'rice']);
|
||||||
console.log('TEST: Suggestion displayed and API called with correct args');
|
console.log('TEST: Suggestion displayed and API called with correct args');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,7 +89,7 @@ describe('RecipeSuggester Component', () => {
|
|||||||
|
|
||||||
// Mock API failure response
|
// Mock API failure response
|
||||||
const errorMessage = 'Invalid ingredients provided.';
|
const errorMessage = 'Invalid ingredients provided.';
|
||||||
vi.mocked(suggestRecipe).mockResolvedValue({
|
mockedApiClient.suggestRecipe.mockResolvedValue({
|
||||||
ok: false,
|
ok: false,
|
||||||
json: async () => ({ message: errorMessage }),
|
json: async () => ({ message: errorMessage }),
|
||||||
} as Response);
|
} as Response);
|
||||||
@@ -117,7 +116,7 @@ describe('RecipeSuggester Component', () => {
|
|||||||
|
|
||||||
// Mock network error
|
// Mock network error
|
||||||
const networkError = new Error('Network Error');
|
const networkError = new Error('Network Error');
|
||||||
vi.mocked(suggestRecipe).mockRejectedValue(networkError);
|
mockedApiClient.suggestRecipe.mockRejectedValue(networkError);
|
||||||
|
|
||||||
const button = screen.getByRole('button', { name: /Suggest a Recipe/i });
|
const button = screen.getByRole('button', { name: /Suggest a Recipe/i });
|
||||||
await user.click(button);
|
await user.click(button);
|
||||||
@@ -148,7 +147,7 @@ describe('RecipeSuggester Component', () => {
|
|||||||
await user.type(input, 'tofu');
|
await user.type(input, 'tofu');
|
||||||
|
|
||||||
// Mock success for the second click
|
// Mock success for the second click
|
||||||
vi.mocked(suggestRecipe).mockResolvedValue({
|
mockedApiClient.suggestRecipe.mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: async () => ({ suggestion: 'Tofu Stir Fry' }),
|
json: async () => ({ suggestion: 'Tofu Stir Fry' }),
|
||||||
} as Response);
|
} as Response);
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|||||||
import { useFlyerItems } from './useFlyerItems';
|
import { useFlyerItems } from './useFlyerItems';
|
||||||
import { useApiOnMount } from './useApiOnMount';
|
import { useApiOnMount } from './useApiOnMount';
|
||||||
import { createMockFlyer, createMockFlyerItem } from '../tests/utils/mockFactories';
|
import { createMockFlyer, createMockFlyerItem } from '../tests/utils/mockFactories';
|
||||||
import * as apiClient from '../services/apiClient';
|
|
||||||
|
|
||||||
// Mock the underlying useApiOnMount hook to isolate the useFlyerItems hook's logic.
|
// Mock the underlying useApiOnMount hook to isolate the useFlyerItems hook's logic.
|
||||||
vi.mock('./useApiOnMount');
|
vi.mock('./useApiOnMount');
|
||||||
vi.mock('../services/apiClient');
|
|
||||||
|
|
||||||
const mockedUseApiOnMount = vi.mocked(useApiOnMount);
|
const mockedUseApiOnMount = vi.mocked(useApiOnMount);
|
||||||
|
|
||||||
@@ -61,7 +59,6 @@ describe('useFlyerItems Hook', () => {
|
|||||||
expect(result.current.flyerItems).toEqual([]);
|
expect(result.current.flyerItems).toEqual([]);
|
||||||
expect(result.current.isLoading).toBe(false);
|
expect(result.current.isLoading).toBe(false);
|
||||||
expect(result.current.error).toBeNull();
|
expect(result.current.error).toBeNull();
|
||||||
|
|
||||||
// Assert: Check that useApiOnMount was called with `enabled: false`.
|
// Assert: Check that useApiOnMount was called with `enabled: false`.
|
||||||
expect(mockedUseApiOnMount).toHaveBeenCalledWith(
|
expect(mockedUseApiOnMount).toHaveBeenCalledWith(
|
||||||
expect.any(Function), // the wrapped fetcher function
|
expect.any(Function), // the wrapped fetcher function
|
||||||
@@ -172,7 +169,7 @@ describe('useFlyerItems Hook', () => {
|
|||||||
const wrappedFetcher = mockedUseApiOnMount.mock.calls[0][0];
|
const wrappedFetcher = mockedUseApiOnMount.mock.calls[0][0];
|
||||||
const mockResponse = new Response();
|
const mockResponse = new Response();
|
||||||
vi.mocked(apiClient.fetchFlyerItems).mockResolvedValue(mockResponse);
|
vi.mocked(apiClient.fetchFlyerItems).mockResolvedValue(mockResponse);
|
||||||
|
//FIX: Missing apiClient import here
|
||||||
const response = await wrappedFetcher(123);
|
const response = await wrappedFetcher(123);
|
||||||
|
|
||||||
expect(apiClient.fetchFlyerItems).toHaveBeenCalledWith(123);
|
expect(apiClient.fetchFlyerItems).toHaveBeenCalledWith(123);
|
||||||
@@ -180,3 +177,6 @@ describe('useFlyerItems Hook', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
import * as apiClient from '../services/apiClient';
|
||||||
|
|
||||||
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ type MockApiResult = {
|
|||||||
vi.mock('./useApi');
|
vi.mock('./useApi');
|
||||||
vi.mock('../hooks/useAuth');
|
vi.mock('../hooks/useAuth');
|
||||||
vi.mock('../hooks/useUserData');
|
vi.mock('../hooks/useUserData');
|
||||||
vi.mock('../services/apiClient');
|
|
||||||
|
|
||||||
// The apiClient is globally mocked in our test setup, so we just need to cast it
|
// The apiClient is globally mocked in our test setup, so we just need to cast it
|
||||||
const mockedUseApi = vi.mocked(useApi);
|
const mockedUseApi = vi.mocked(useApi);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
vi.mock('./useApi');
|
vi.mock('./useApi');
|
||||||
vi.mock('../hooks/useAuth');
|
vi.mock('../hooks/useAuth');
|
||||||
vi.mock('../hooks/useUserData');
|
vi.mock('../hooks/useUserData');
|
||||||
vi.mock('../services/apiClient');
|
|
||||||
|
|
||||||
// The apiClient is globally mocked in our test setup, so we just need to cast it
|
// The apiClient is globally mocked in our test setup, so we just need to cast it
|
||||||
const mockedUseApi = vi.mocked(useApi);
|
const mockedUseApi = vi.mocked(useApi);
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
// src/components/MyDealsPage.test.tsx
|
// src/pages/MyDealsPage.test.tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, screen, waitFor } from '@testing-library/react';
|
import { render, screen, waitFor } from '@testing-library/react';
|
||||||
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
||||||
import MyDealsPage from './MyDealsPage';
|
import MyDealsPage from './MyDealsPage';
|
||||||
import * as apiClient from '../services/apiClient';
|
import * as apiClient from '../services/apiClient';
|
||||||
import { WatchedItemDeal } from '../types';
|
import type { WatchedItemDeal } from '../types';
|
||||||
import { logger } from '../services/logger.client';
|
import { logger } from '../services/logger.client';
|
||||||
import { createMockWatchedItemDeal } from '../tests/utils/mockFactories';
|
import { createMockWatchedItemDeal } from '../tests/utils/mockFactories';
|
||||||
|
|
||||||
// Mock the apiClient. The component now directly uses `fetchBestSalePrices`.
|
// The apiClient is mocked globally in `src/tests/setup/globalApiMock.ts`.
|
||||||
// By mocking the entire module, we can control the behavior of `fetchBestSalePrices`
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
// for our tests.
|
|
||||||
vi.mock('../services/apiClient');
|
|
||||||
const mockedApiClient = apiClient as Mocked<typeof apiClient>;
|
|
||||||
|
|
||||||
// Mock the logger
|
// Mock the logger
|
||||||
vi.mock('../services/logger.client', () => ({
|
vi.mock('../services/logger.client', () => ({
|
||||||
|
|||||||
@@ -10,13 +10,7 @@ import { logger } from '../services/logger.client';
|
|||||||
// The apiClient and logger are now mocked globally.
|
// The apiClient and logger are now mocked globally.
|
||||||
const mockedApiClient = vi.mocked(apiClient);
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|
||||||
vi.mock('../services/logger.client', () => ({
|
// The logger is mocked globally.
|
||||||
logger: {
|
|
||||||
info: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Helper function to render the component within a router context
|
// Helper function to render the component within a router context
|
||||||
const renderWithRouter = (token: string) => {
|
const renderWithRouter = (token: string) => {
|
||||||
return render(
|
return render(
|
||||||
|
|||||||
@@ -11,16 +11,8 @@ import {
|
|||||||
createMockUser,
|
createMockUser,
|
||||||
} from '../tests/utils/mockFactories';
|
} from '../tests/utils/mockFactories';
|
||||||
|
|
||||||
// Mock dependencies
|
// The apiClient, logger, notificationService, and aiApiClient are all mocked globally.
|
||||||
vi.mock('../services/apiClient'); // This was correct
|
// We can get a typed reference to the notificationService for individual test overrides.
|
||||||
vi.mock('../services/logger.client', () => ({
|
|
||||||
logger: {
|
|
||||||
info: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
vi.mock('../services/notificationService');
|
|
||||||
vi.mock('../services/aiApiClient'); // Mock aiApiClient as it's used in the component
|
|
||||||
const mockedNotificationService = vi.mocked(await import('../services/notificationService'));
|
const mockedNotificationService = vi.mocked(await import('../services/notificationService'));
|
||||||
vi.mock('../components/AchievementsList', () => ({
|
vi.mock('../components/AchievementsList', () => ({
|
||||||
AchievementsList: ({ achievements }: { achievements: (UserAchievement & Achievement)[] }) => (
|
AchievementsList: ({ achievements }: { achievements: (UserAchievement & Achievement)[] }) => (
|
||||||
@@ -28,7 +20,7 @@ vi.mock('../components/AchievementsList', () => ({
|
|||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockedApiClient = apiClient as Mocked<typeof apiClient>;
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|
||||||
// --- Mock Data ---
|
// --- Mock Data ---
|
||||||
const mockProfile: UserProfile = createMockUserProfile({
|
const mockProfile: UserProfile = createMockUserProfile({
|
||||||
|
|||||||
@@ -10,21 +10,10 @@ import { logger } from '../services/logger.client';
|
|||||||
// Extensive logging for debugging
|
// Extensive logging for debugging
|
||||||
const LOG_PREFIX = '[TEST DEBUG]';
|
const LOG_PREFIX = '[TEST DEBUG]';
|
||||||
|
|
||||||
vi.mock('../services/notificationService');
|
// The aiApiClient, notificationService, and logger are mocked globally.
|
||||||
|
// We can get a typed reference to the aiApiClient for individual test overrides.
|
||||||
// 1. Mock the module to replace its exports with mock functions.
|
|
||||||
vi.mock('../services/aiApiClient');
|
|
||||||
// 2. Get a typed reference to the mocked module to control its functions in tests.
|
|
||||||
const mockedAiApiClient = vi.mocked(aiApiClient);
|
const mockedAiApiClient = vi.mocked(aiApiClient);
|
||||||
|
|
||||||
// Mock the logger
|
|
||||||
vi.mock('../services/logger.client', () => ({
|
|
||||||
logger: {
|
|
||||||
info: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Define mock at module level so it can be referenced in the implementation
|
// Define mock at module level so it can be referenced in the implementation
|
||||||
const mockAudioPlay = vi.fn(() => {
|
const mockAudioPlay = vi.fn(() => {
|
||||||
console.log(`${LOG_PREFIX} mockAudioPlay executed`);
|
console.log(`${LOG_PREFIX} mockAudioPlay executed`);
|
||||||
|
|||||||
@@ -6,16 +6,9 @@ import { MemoryRouter } from 'react-router-dom';
|
|||||||
import * as apiClient from '../../services/apiClient';
|
import * as apiClient from '../../services/apiClient';
|
||||||
import { logger } from '../../services/logger.client';
|
import { logger } from '../../services/logger.client';
|
||||||
|
|
||||||
// Mock dependencies
|
// The apiClient and logger are mocked globally.
|
||||||
vi.mock('../../services/apiClient', () => ({
|
// We can get a typed reference to the apiClient for individual test overrides.
|
||||||
getFlyersForReview: vi.fn(),
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('../../services/logger.client', () => ({
|
|
||||||
logger: {
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock LoadingSpinner to simplify DOM and avoid potential issues
|
// Mock LoadingSpinner to simplify DOM and avoid potential issues
|
||||||
vi.mock('../../components/LoadingSpinner', () => ({
|
vi.mock('../../components/LoadingSpinner', () => ({
|
||||||
@@ -29,7 +22,7 @@ describe('FlyerReviewPage', () => {
|
|||||||
|
|
||||||
it('renders loading spinner initially', () => {
|
it('renders loading spinner initially', () => {
|
||||||
// Mock a promise that doesn't resolve immediately to check loading state
|
// Mock a promise that doesn't resolve immediately to check loading state
|
||||||
vi.mocked(apiClient.getFlyersForReview).mockReturnValue(new Promise(() => {}));
|
mockedApiClient.getFlyersForReview.mockReturnValue(new Promise(() => {}));
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
@@ -41,7 +34,7 @@ describe('FlyerReviewPage', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders empty state when no flyers are returned', async () => {
|
it('renders empty state when no flyers are returned', async () => {
|
||||||
vi.mocked(apiClient.getFlyersForReview).mockResolvedValue({
|
mockedApiClient.getFlyersForReview.mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: async () => [],
|
json: async () => [],
|
||||||
} as Response);
|
} as Response);
|
||||||
@@ -84,7 +77,7 @@ describe('FlyerReviewPage', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
vi.mocked(apiClient.getFlyersForReview).mockResolvedValue({
|
mockedApiClient.getFlyersForReview.mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: async () => mockFlyers,
|
json: async () => mockFlyers,
|
||||||
} as Response);
|
} as Response);
|
||||||
@@ -114,7 +107,7 @@ describe('FlyerReviewPage', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders error message when API response is not ok', async () => {
|
it('renders error message when API response is not ok', async () => {
|
||||||
vi.mocked(apiClient.getFlyersForReview).mockResolvedValue({
|
mockedApiClient.getFlyersForReview.mockResolvedValue({
|
||||||
ok: false,
|
ok: false,
|
||||||
json: async () => ({ message: 'Server error' }),
|
json: async () => ({ message: 'Server error' }),
|
||||||
} as Response);
|
} as Response);
|
||||||
@@ -138,7 +131,7 @@ describe('FlyerReviewPage', () => {
|
|||||||
|
|
||||||
it('renders error message when API throws an error', async () => {
|
it('renders error message when API throws an error', async () => {
|
||||||
const networkError = new Error('Network error');
|
const networkError = new Error('Network error');
|
||||||
vi.mocked(apiClient.getFlyersForReview).mockRejectedValue(networkError);
|
mockedApiClient.getFlyersForReview.mockRejectedValue(networkError);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
@@ -159,7 +152,7 @@ describe('FlyerReviewPage', () => {
|
|||||||
|
|
||||||
it('renders a generic error for non-Error rejections', async () => {
|
it('renders a generic error for non-Error rejections', async () => {
|
||||||
const nonErrorRejection = { message: 'This is not an Error object' };
|
const nonErrorRejection = { message: 'This is not an Error object' };
|
||||||
vi.mocked(apiClient.getFlyersForReview).mockRejectedValue(nonErrorRejection);
|
mockedApiClient.getFlyersForReview.mockRejectedValue(nonErrorRejection);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
|
|||||||
@@ -12,14 +12,9 @@ import {
|
|||||||
} from '../../../tests/utils/mockFactories';
|
} from '../../../tests/utils/mockFactories';
|
||||||
import { renderWithProviders } from '../../../tests/utils/renderWithProviders';
|
import { renderWithProviders } from '../../../tests/utils/renderWithProviders';
|
||||||
|
|
||||||
// Cast the mocked module to its mocked type to retain type safety and autocompletion.
|
// The apiClient and logger are mocked globally.
|
||||||
// The apiClient is now mocked globally via src/tests/setup/tests-setup-unit.ts.
|
// We can get a typed reference to the apiClient for individual test overrides.
|
||||||
const mockedApiClient = apiClient as Mocked<typeof apiClient>;
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|
||||||
// Mock the logger
|
|
||||||
vi.mock('../../../services/logger', () => ({
|
|
||||||
logger: { info: vi.fn(), error: vi.fn() },
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock the ConfirmationModal to test its props and interactions
|
// Mock the ConfirmationModal to test its props and interactions
|
||||||
// The ConfirmationModal is now in a different directory.
|
// The ConfirmationModal is now in a different directory.
|
||||||
|
|||||||
@@ -21,25 +21,10 @@ vi.mock('../../../components/PasswordInput', () => ({
|
|||||||
PasswordInput: (props: any) => <input {...props} data-testid="password-input" />,
|
PasswordInput: (props: any) => <input {...props} data-testid="password-input" />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// The apiClient, notificationService, react-hot-toast, and logger are all mocked globally.
|
||||||
|
// We can get a typed reference to the apiClient for individual test overrides.
|
||||||
const mockedApiClient = vi.mocked(apiClient, true);
|
const mockedApiClient = vi.mocked(apiClient, true);
|
||||||
|
|
||||||
vi.mock('../../../services/notificationService');
|
|
||||||
vi.mock('react-hot-toast', () => ({
|
|
||||||
__esModule: true,
|
|
||||||
default: {
|
|
||||||
success: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
vi.mock('../../../services/logger.client', () => ({
|
|
||||||
logger: {
|
|
||||||
debug: vi.fn(),
|
|
||||||
info: vi.fn(),
|
|
||||||
warn: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const mockOnClose = vi.fn();
|
const mockOnClose = vi.fn();
|
||||||
const mockOnLoginSuccess = vi.fn();
|
const mockOnLoginSuccess = vi.fn();
|
||||||
const mockOnSignOut = vi.fn();
|
const mockOnSignOut = vi.fn();
|
||||||
|
|||||||
@@ -8,46 +8,11 @@ import toast from 'react-hot-toast';
|
|||||||
import { createMockUser } from '../../../tests/utils/mockFactories';
|
import { createMockUser } from '../../../tests/utils/mockFactories';
|
||||||
import { renderWithProviders } from '../../../tests/utils/renderWithProviders';
|
import { renderWithProviders } from '../../../tests/utils/renderWithProviders';
|
||||||
|
|
||||||
// Mock the entire apiClient module to ensure all exports are defined.
|
// The apiClient is mocked globally in `src/tests/setup/globalApiMock.ts`.
|
||||||
// This is the primary fix for the error: [vitest] No "..." export is defined on the mock.
|
// We can get a type-safe mocked version of the module to override functions for specific tests.
|
||||||
vi.mock('../../../services/apiClient', () => ({
|
|
||||||
// Mocks for providers used by renderWithProviders
|
|
||||||
fetchFlyers: vi.fn(),
|
|
||||||
fetchMasterItems: vi.fn(),
|
|
||||||
fetchWatchedItems: vi.fn(),
|
|
||||||
fetchShoppingLists: vi.fn(),
|
|
||||||
getAuthenticatedUserProfile: vi.fn(),
|
|
||||||
pingBackend: vi.fn(),
|
|
||||||
checkStorage: vi.fn(),
|
|
||||||
checkDbPoolHealth: vi.fn(),
|
|
||||||
checkPm2Status: vi.fn(),
|
|
||||||
checkRedisHealth: vi.fn(),
|
|
||||||
checkDbSchema: vi.fn(),
|
|
||||||
loginUser: vi.fn(),
|
|
||||||
triggerFailingJob: vi.fn(),
|
|
||||||
clearGeocodeCache: vi.fn(),
|
|
||||||
}));
|
|
||||||
// Get a type-safe mocked version of the apiClient module.
|
|
||||||
const mockedApiClient = vi.mocked(apiClient);
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|
||||||
// Correct the relative path to the logger module.
|
// The logger and react-hot-toast are mocked globally.
|
||||||
vi.mock('../../../services/logger', () => ({
|
|
||||||
logger: {
|
|
||||||
info: vi.fn(),
|
|
||||||
warn: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
debug: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock toast to check for notifications
|
|
||||||
vi.mock('react-hot-toast', () => ({
|
|
||||||
__esModule: true,
|
|
||||||
default: {
|
|
||||||
success: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('SystemCheck', () => {
|
describe('SystemCheck', () => {
|
||||||
// Store original env variable
|
// Store original env variable
|
||||||
|
|||||||
@@ -6,14 +6,8 @@ import { ApiProvider } from './ApiProvider';
|
|||||||
import { ApiContext } from '../contexts/ApiContext';
|
import { ApiContext } from '../contexts/ApiContext';
|
||||||
import * as apiClient from '../services/apiClient';
|
import * as apiClient from '../services/apiClient';
|
||||||
|
|
||||||
// Mock the apiClient module.
|
// The apiClient is mocked globally in `src/tests/setup/globalApiMock.ts`.
|
||||||
// Since ApiProvider and ApiContext import * as apiClient, mocking it ensures
|
// This test verifies that the ApiProvider correctly provides this mocked module.
|
||||||
// we control the reference identity and can verify it's being passed correctly.
|
|
||||||
vi.mock('../services/apiClient', () => ({
|
|
||||||
fetchFlyers: vi.fn(),
|
|
||||||
fetchMasterItems: vi.fn(),
|
|
||||||
// Add other mocked methods as needed for the shape to be valid-ish
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('ApiProvider & ApiContext', () => {
|
describe('ApiProvider & ApiContext', () => {
|
||||||
const TestConsumer = () => {
|
const TestConsumer = () => {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { render, screen, waitFor, fireEvent, act } from '@testing-library/react'
|
|||||||
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
||||||
import { AuthProvider } from './AuthProvider';
|
import { AuthProvider } from './AuthProvider';
|
||||||
import { AuthContext } from '../contexts/AuthContext';
|
import { AuthContext } from '../contexts/AuthContext';
|
||||||
import * as apiClient from '../services/apiClient';
|
|
||||||
import * as tokenStorage from '../services/tokenStorage';
|
import * as tokenStorage from '../services/tokenStorage';
|
||||||
import { createMockUserProfile } from '../tests/utils/mockFactories';
|
import { createMockUserProfile } from '../tests/utils/mockFactories';
|
||||||
|
import * as apiClient from '../services/apiClient';
|
||||||
|
|
||||||
// Mocks
|
// Mocks
|
||||||
vi.mock('../services/apiClient');
|
// The apiClient is mocked globally in `src/tests/setup/globalApiMock.ts`.
|
||||||
vi.mock('../services/tokenStorage');
|
vi.mock('../services/tokenStorage');
|
||||||
vi.mock('../services/logger.client', () => ({
|
vi.mock('../services/logger.client', () => ({
|
||||||
logger: {
|
logger: {
|
||||||
@@ -20,7 +20,7 @@ vi.mock('../services/logger.client', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockedApiClient = apiClient as Mocked<typeof apiClient>;
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
const mockedTokenStorage = tokenStorage as Mocked<typeof tokenStorage>;
|
const mockedTokenStorage = tokenStorage as Mocked<typeof tokenStorage>;
|
||||||
|
|
||||||
const mockProfile = createMockUserProfile({
|
const mockProfile = createMockUserProfile({
|
||||||
|
|||||||
65
src/tests/setup/globalApiMock.ts
Normal file
65
src/tests/setup/globalApiMock.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// src/tests/setup/globalApiMock.ts
|
||||||
|
import { vi } from 'vitest';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks the entire apiClient module.
|
||||||
|
* This global mock is loaded for all tests via the `setupFiles` config in vitest.config.ts.
|
||||||
|
* It prevents test failures in components that use providers (like FlyersProvider, AuthProvider)
|
||||||
|
* which make API calls on mount when using `renderWithProviders`.
|
||||||
|
*
|
||||||
|
* Individual tests can override specific functions as needed, for example:
|
||||||
|
*
|
||||||
|
* import { vi } from 'vitest';
|
||||||
|
* import * as apiClient from '../services/apiClient';
|
||||||
|
*
|
||||||
|
* const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
*
|
||||||
|
* it('should test something', () => {
|
||||||
|
* mockedApiClient.someFunction.mockResolvedValue({ ... });
|
||||||
|
* // ... rest of the test
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
vi.mock('../../services/apiClient', () => ({
|
||||||
|
// --- Provider Mocks (with default successful responses) ---
|
||||||
|
// These are essential for any test using renderWithProviders, as AppProviders
|
||||||
|
// will mount all these data providers.
|
||||||
|
fetchFlyers: vi.fn(() => Promise.resolve(new Response(JSON.stringify({ flyers: [], hasMore: false })))),
|
||||||
|
fetchMasterItems: vi.fn(() => Promise.resolve(new Response(JSON.stringify([])))),
|
||||||
|
fetchWatchedItems: vi.fn(() => Promise.resolve(new Response(JSON.stringify([])))),
|
||||||
|
fetchShoppingLists: vi.fn(() => Promise.resolve(new Response(JSON.stringify([])))),
|
||||||
|
getAuthenticatedUserProfile: vi.fn(() => Promise.resolve(new Response(JSON.stringify(null)))),
|
||||||
|
fetchCategories: vi.fn(() => Promise.resolve(new Response(JSON.stringify([])))), // For CorrectionsPage
|
||||||
|
fetchAllBrands: vi.fn(() => Promise.resolve(new Response(JSON.stringify([])))), // For AdminBrandManager
|
||||||
|
|
||||||
|
// --- General Mocks (return empty vi.fn() by default) ---
|
||||||
|
// These functions are commonly used and can be implemented in specific tests.
|
||||||
|
suggestRecipe: vi.fn(),
|
||||||
|
getApplicationStats: vi.fn(),
|
||||||
|
getSuggestedCorrections: vi.fn(),
|
||||||
|
approveCorrection: vi.fn(),
|
||||||
|
rejectCorrection: vi.fn(),
|
||||||
|
updateSuggestedCorrection: vi.fn(),
|
||||||
|
pingBackend: vi.fn(),
|
||||||
|
checkStorage: vi.fn(),
|
||||||
|
checkDbPoolHealth: vi.fn(),
|
||||||
|
checkPm2Status: vi.fn(),
|
||||||
|
checkRedisHealth: vi.fn(),
|
||||||
|
checkDbSchema: vi.fn(),
|
||||||
|
loginUser: vi.fn(),
|
||||||
|
registerUser: vi.fn(),
|
||||||
|
requestPasswordReset: vi.fn(),
|
||||||
|
triggerFailingJob: vi.fn(),
|
||||||
|
clearGeocodeCache: vi.fn(),
|
||||||
|
uploadBrandLogo: vi.fn(),
|
||||||
|
fetchActivityLog: vi.fn(),
|
||||||
|
updateUserProfile: vi.fn(),
|
||||||
|
updateUserPassword: vi.fn(),
|
||||||
|
updateUserPreferences: vi.fn(),
|
||||||
|
exportUserData: vi.fn(),
|
||||||
|
deleteUserAccount: vi.fn(),
|
||||||
|
getUserAddress: vi.fn(),
|
||||||
|
updateUserAddress: vi.fn(),
|
||||||
|
geocodeAddress: vi.fn(),
|
||||||
|
getFlyersForReview: vi.fn(),
|
||||||
|
fetchLeaderboard: vi.fn(),
|
||||||
|
}));
|
||||||
@@ -257,67 +257,6 @@ vi.mock('@google/genai', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Mocks the entire apiClient module.
|
|
||||||
* This ensures that all test files that import from apiClient will get this mocked version.
|
|
||||||
*/
|
|
||||||
vi.mock('../../services/apiClient', () => ({
|
|
||||||
// --- Auth ---
|
|
||||||
registerUser: vi.fn(),
|
|
||||||
loginUser: vi.fn(),
|
|
||||||
getAuthenticatedUserProfile: vi.fn(),
|
|
||||||
requestPasswordReset: vi.fn(),
|
|
||||||
resetPassword: vi.fn(),
|
|
||||||
updateUserPassword: vi.fn(),
|
|
||||||
deleteUserAccount: vi.fn(),
|
|
||||||
updateUserPreferences: vi.fn(),
|
|
||||||
updateUserProfile: vi.fn(),
|
|
||||||
// --- Data Fetching & Manipulation ---
|
|
||||||
fetchFlyers: vi.fn(),
|
|
||||||
fetchFlyerItems: vi.fn(),
|
|
||||||
// Provide a default implementation that returns a valid Response object to prevent timeouts.
|
|
||||||
fetchFlyerItemsForFlyers: vi.fn(() => Promise.resolve(new Response(JSON.stringify([])))),
|
|
||||||
countFlyerItemsForFlyers: vi.fn(() =>
|
|
||||||
Promise.resolve(new Response(JSON.stringify({ count: 0 }))),
|
|
||||||
),
|
|
||||||
fetchMasterItems: vi.fn(),
|
|
||||||
fetchWatchedItems: vi.fn(),
|
|
||||||
addWatchedItem: vi.fn(),
|
|
||||||
removeWatchedItem: vi.fn(),
|
|
||||||
fetchShoppingLists: vi.fn(),
|
|
||||||
createShoppingList: vi.fn(),
|
|
||||||
deleteShoppingList: vi.fn(),
|
|
||||||
addShoppingListItem: vi.fn(),
|
|
||||||
updateShoppingListItem: vi.fn(),
|
|
||||||
removeShoppingListItem: vi.fn(),
|
|
||||||
fetchHistoricalPriceData: vi.fn(),
|
|
||||||
processFlyerFile: vi.fn(),
|
|
||||||
uploadLogoAndUpdateStore: vi.fn(),
|
|
||||||
exportUserData: vi.fn(),
|
|
||||||
// --- Address ---
|
|
||||||
getUserAddress: vi.fn(),
|
|
||||||
updateUserAddress: vi.fn(),
|
|
||||||
geocodeAddress: vi.fn(() => Promise.resolve(new Response(JSON.stringify({ lat: 0, lng: 0 })))),
|
|
||||||
// --- Admin ---
|
|
||||||
getSuggestedCorrections: vi.fn(),
|
|
||||||
fetchCategories: vi.fn(),
|
|
||||||
approveCorrection: vi.fn(),
|
|
||||||
rejectCorrection: vi.fn(),
|
|
||||||
updateSuggestedCorrection: vi.fn(),
|
|
||||||
getApplicationStats: vi.fn(),
|
|
||||||
fetchActivityLog: vi.fn(),
|
|
||||||
fetchAllBrands: vi.fn(),
|
|
||||||
uploadBrandLogo: vi.fn(),
|
|
||||||
// --- System ---
|
|
||||||
pingBackend: vi.fn(),
|
|
||||||
checkDbSchema: vi.fn(),
|
|
||||||
checkStorage: vi.fn(),
|
|
||||||
checkDbPoolHealth: vi.fn(),
|
|
||||||
checkRedisHealth: vi.fn(),
|
|
||||||
checkPm2Status: vi.fn(),
|
|
||||||
fetchLeaderboard: vi.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// FIX: Mock the aiApiClient module as well, which is used by AnalysisPanel
|
// FIX: Mock the aiApiClient module as well, which is used by AnalysisPanel
|
||||||
vi.mock('../../services/aiApiClient', () => ({
|
vi.mock('../../services/aiApiClient', () => ({
|
||||||
// Provide a default implementation that returns a valid Response object to prevent timeouts.
|
// Provide a default implementation that returns a valid Response object to prevent timeouts.
|
||||||
|
|||||||
102
src/types/exif-parser.d.ts
vendored
102
src/types/exif-parser.d.ts
vendored
@@ -5,4 +5,104 @@
|
|||||||
* which does not ship with its own TypeScript types. This allows TypeScript
|
* which does not ship with its own TypeScript types. This allows TypeScript
|
||||||
* to recognize it as a module and avoids "implicit any" errors.
|
* to recognize it as a module and avoids "implicit any" errors.
|
||||||
*/
|
*/
|
||||||
declare module 'exif-parser';
|
declare module 'exif-parser' {
|
||||||
|
/**
|
||||||
|
* Represents the size of the image.
|
||||||
|
*/
|
||||||
|
export interface ImageSize {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents thumbnail data if available.
|
||||||
|
*/
|
||||||
|
export interface Thumbnail {
|
||||||
|
format: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
offset: number;
|
||||||
|
size: number;
|
||||||
|
buffer: Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents GPS information if available.
|
||||||
|
*/
|
||||||
|
export interface GPS {
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
altitude: number;
|
||||||
|
latitudeRef: string;
|
||||||
|
longitudeRef: string;
|
||||||
|
altitudeRef: number;
|
||||||
|
GPSDateStamp: string;
|
||||||
|
GPSTimeStamp: number[]; // [hour, minute, second]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the parsed EXIF data structure.
|
||||||
|
* This includes common tags and derived properties.
|
||||||
|
*/
|
||||||
|
export interface ExifData {
|
||||||
|
/**
|
||||||
|
* A dictionary of raw EXIF tags. Keys are tag names (e.g., 'Make', 'Model', 'DateTimeOriginal').
|
||||||
|
* Values can be of various types (string, number, Date, etc.).
|
||||||
|
*/
|
||||||
|
tags: {
|
||||||
|
Make?: string;
|
||||||
|
Model?: string;
|
||||||
|
Orientation?: number;
|
||||||
|
XResolution?: number;
|
||||||
|
YResolution?: number;
|
||||||
|
ResolutionUnit?: number;
|
||||||
|
DateTimeOriginal?: Date; // Parsed into a Date object
|
||||||
|
DateTimeDigitized?: Date;
|
||||||
|
ExposureTime?: number;
|
||||||
|
FNumber?: number;
|
||||||
|
ISOSpeedRatings?: number;
|
||||||
|
ShutterSpeedValue?: number;
|
||||||
|
ApertureValue?: number;
|
||||||
|
BrightnessValue?: number;
|
||||||
|
ExposureBiasValue?: number;
|
||||||
|
MaxApertureValue?: number;
|
||||||
|
MeteringMode?: number;
|
||||||
|
LightSource?: number;
|
||||||
|
Flash?: number;
|
||||||
|
FocalLength?: number;
|
||||||
|
ColorSpace?: number;
|
||||||
|
ExifImageWidth?: number;
|
||||||
|
ExifImageHeight?: number;
|
||||||
|
ExposureMode?: number;
|
||||||
|
WhiteBalance?: number;
|
||||||
|
DigitalZoomRatio?: number;
|
||||||
|
FocalLengthIn35mmFilm?: number;
|
||||||
|
SceneCaptureType?: number;
|
||||||
|
GainControl?: number;
|
||||||
|
Contrast?: number;
|
||||||
|
Saturation?: number;
|
||||||
|
Sharpness?: number;
|
||||||
|
SubjectDistanceRange?: number;
|
||||||
|
GPSVersionID?: number[];
|
||||||
|
GPSLatitudeRef?: string;
|
||||||
|
GPSLatitude?: number[];
|
||||||
|
GPSLongitudeRef?: string;
|
||||||
|
GPSLongitude?: number[];
|
||||||
|
GPSAltitudeRef?: number;
|
||||||
|
GPSAltitude?: number;
|
||||||
|
GPSTimeStamp?: number[];
|
||||||
|
GPSDateStamp?: string;
|
||||||
|
[key: string]: any; // Allow for other, less common tags
|
||||||
|
};
|
||||||
|
imageSize: ImageSize;
|
||||||
|
thumbnail?: Thumbnail;
|
||||||
|
gps?: GPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExifParser {
|
||||||
|
static create(buffer: Buffer): ExifParser;
|
||||||
|
parse(): ExifData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExifParser;
|
||||||
|
}
|
||||||
80
src/types/pdf-poppler.d.ts
vendored
80
src/types/pdf-poppler.d.ts
vendored
@@ -7,37 +7,115 @@
|
|||||||
* structure, preventing import errors and enabling type checking.
|
* structure, preventing import errors and enabling type checking.
|
||||||
*/
|
*/
|
||||||
declare module 'pdf-poppler' {
|
declare module 'pdf-poppler' {
|
||||||
|
/**
|
||||||
|
* Defines the options available for the main `convert` method.
|
||||||
|
* This appears to be a simplified wrapper around pdftocairo.
|
||||||
|
*/
|
||||||
|
export interface ConvertOptions {
|
||||||
|
/**
|
||||||
|
* The output image format.
|
||||||
|
*/
|
||||||
|
format?: 'jpeg' | 'png' | 'tiff';
|
||||||
|
/**
|
||||||
|
* The directory where output images will be saved.
|
||||||
|
*/
|
||||||
|
out_dir?: string;
|
||||||
|
/**
|
||||||
|
* The prefix for the output image files.
|
||||||
|
*/
|
||||||
|
out_prefix?: string;
|
||||||
|
/**
|
||||||
|
* Specify a page number to convert a specific page, or null to convert all pages.
|
||||||
|
*/
|
||||||
|
page?: number | null;
|
||||||
|
/**
|
||||||
|
* Specifies the resolution, in DPI. The default is 72 DPI.
|
||||||
|
*/
|
||||||
|
resolution?: number;
|
||||||
|
/**
|
||||||
|
* Scales each page to fit in scale-to x scale-to pixel square.
|
||||||
|
*/
|
||||||
|
scale_to?: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the options available for the pdfToCairo conversion method.
|
* Defines the options available for the pdfToCairo conversion method.
|
||||||
* This interface can be expanded as more options are used.
|
* These options correspond to the command-line arguments for the `pdftocairo` utility.
|
||||||
*/
|
*/
|
||||||
export interface PopplerOptions {
|
export interface PopplerOptions {
|
||||||
antialias?: 'default' | 'gray' | 'none' | 'subpixel';
|
antialias?: 'default' | 'gray' | 'none' | 'subpixel';
|
||||||
cropBox?: boolean;
|
cropBox?: boolean;
|
||||||
cropHeight?: number;
|
cropHeight?: number;
|
||||||
cropWidth?: number;
|
cropWidth?: number;
|
||||||
|
cropSize?: number;
|
||||||
cropX?: number;
|
cropX?: number;
|
||||||
cropY?: number;
|
cropY?: number;
|
||||||
|
duplex?: boolean;
|
||||||
|
epsFile?: boolean;
|
||||||
|
expand?: boolean;
|
||||||
firstPage?: number;
|
firstPage?: number;
|
||||||
|
grayFile?: boolean;
|
||||||
lastPage?: number;
|
lastPage?: number;
|
||||||
jpegFile?: boolean;
|
jpegFile?: boolean;
|
||||||
jpegOptions?: string;
|
jpegOptions?: string;
|
||||||
|
level2?: boolean;
|
||||||
|
level3?: boolean;
|
||||||
|
monoFile?: boolean;
|
||||||
|
noCenter?: boolean;
|
||||||
noCrop?: boolean;
|
noCrop?: boolean;
|
||||||
noRotate?: boolean;
|
noRotate?: boolean;
|
||||||
|
noShrink?: boolean;
|
||||||
ownerPassword?: string;
|
ownerPassword?: string;
|
||||||
paperHeight?: number;
|
paperHeight?: number;
|
||||||
paperWidth?: number;
|
paperWidth?: number;
|
||||||
paperSize?: 'letter' | 'legal' | 'A4' | 'A3' | 'match';
|
paperSize?: 'letter' | 'legal' | 'A4' | 'A3' | 'match';
|
||||||
pngFile?: boolean;
|
pngFile?: boolean;
|
||||||
|
psFile?: boolean;
|
||||||
|
pdfFile?: boolean;
|
||||||
resolution?: number;
|
resolution?: number;
|
||||||
|
scaleTo?: number;
|
||||||
|
scaleToX?: number;
|
||||||
|
scaleToY?: number;
|
||||||
svgFile?: boolean;
|
svgFile?: boolean;
|
||||||
|
tiffFile?: boolean;
|
||||||
userPassword?: string;
|
userPassword?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the structure of the PDF information object returned by `pdfInfo`.
|
||||||
|
*/
|
||||||
|
export interface PdfInfo {
|
||||||
|
// Based on common pdfinfo output
|
||||||
|
title: string;
|
||||||
|
author: string;
|
||||||
|
creator: string;
|
||||||
|
producer: string;
|
||||||
|
creationDate: string;
|
||||||
|
modDate: string;
|
||||||
|
tagged: boolean;
|
||||||
|
form: string;
|
||||||
|
pages: number;
|
||||||
|
encrypted: boolean;
|
||||||
|
pageSize: string;
|
||||||
|
fileSize: string;
|
||||||
|
optimized: boolean;
|
||||||
|
pdfVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class Poppler {
|
export class Poppler {
|
||||||
constructor(binPath?: string);
|
constructor(binPath?: string);
|
||||||
pdfToCairo(file: string, outputFilePrefix?: string, options?: PopplerOptions): Promise<string>;
|
pdfToCairo(file: string, outputFilePrefix?: string, options?: PopplerOptions): Promise<string>;
|
||||||
|
pdfInfo(file: string, options?: { ownerPassword?: string; userPassword?: string }): Promise<PdfInfo>;
|
||||||
|
pdfToPs(file: string, outputFile: string, options?: any): Promise<string>;
|
||||||
|
pdfToText(file: string, outputFile: string, options?: any): Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a PDF file to images. This seems to be a convenience function provided by the library.
|
||||||
|
* @param pdfPath The path to the PDF file.
|
||||||
|
* @param options The conversion options.
|
||||||
|
*/
|
||||||
|
export function convert(pdfPath: string, options?: ConvertOptions): Promise<string>;
|
||||||
|
|
||||||
export default Poppler;
|
export default Poppler;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,14 @@ export default defineConfig({
|
|||||||
// By default, Vitest does not suppress console logs.
|
// By default, Vitest does not suppress console logs.
|
||||||
// The onConsoleLog hook is only needed if you want to conditionally filter specific logs.
|
// The onConsoleLog hook is only needed if you want to conditionally filter specific logs.
|
||||||
// Keeping the default behavior is often safer to avoid missing important warnings.
|
// Keeping the default behavior is often safer to avoid missing important warnings.
|
||||||
|
|
||||||
environment: 'jsdom',
|
environment: 'jsdom',
|
||||||
// Explicitly point Vitest to the correct tsconfig and enable globals.
|
|
||||||
globals: true, // tsconfig is auto-detected, so the explicit property is not needed and causes an error.
|
globals: true, // tsconfig is auto-detected, so the explicit property is not needed and causes an error.
|
||||||
globalSetup: './src/tests/setup/global-setup.ts',
|
globalSetup: './src/tests/setup/global-setup.ts',
|
||||||
setupFiles: ['./src/tests/setup/tests-setup-unit.ts'],
|
// The globalApiMock MUST come first to ensure it's applied before other mocks that might depend on it.
|
||||||
|
setupFiles: [
|
||||||
|
'./src/tests/setup/globalApiMock.ts',
|
||||||
|
'./src/tests/setup/tests-setup-unit.ts',
|
||||||
|
],
|
||||||
// Explicitly include only test files.
|
// Explicitly include only test files.
|
||||||
// We remove 'src/vite-env.d.ts' which was causing it to be run as a test.
|
// We remove 'src/vite-env.d.ts' which was causing it to be run as a test.
|
||||||
include: ['src/**/*.test.{ts,tsx}'],
|
include: ['src/**/*.test.{ts,tsx}'],
|
||||||
|
|||||||
Reference in New Issue
Block a user