All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 14m41s
241 lines
7.3 KiB
TypeScript
241 lines
7.3 KiB
TypeScript
// src/pages/admin/CorrectionsPage.test.tsx
|
|
import React from 'react';
|
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import { CorrectionsPage } from './CorrectionsPage';
|
|
import { useSuggestedCorrectionsQuery } from '../../hooks/queries/useSuggestedCorrectionsQuery';
|
|
import { useMasterItemsQuery } from '../../hooks/queries/useMasterItemsQuery';
|
|
import { useCategoriesQuery } from '../../hooks/queries/useCategoriesQuery';
|
|
import type { SuggestedCorrection, MasterGroceryItem, Category } from '../../types';
|
|
import {
|
|
createMockSuggestedCorrection,
|
|
createMockMasterGroceryItem,
|
|
createMockCategory,
|
|
} from '../../tests/utils/mockFactories';
|
|
import { QueryWrapper } from '../../tests/utils/renderWithProviders';
|
|
|
|
// Mock the TanStack Query hooks
|
|
vi.mock('../../hooks/queries/useSuggestedCorrectionsQuery');
|
|
vi.mock('../../hooks/queries/useMasterItemsQuery');
|
|
vi.mock('../../hooks/queries/useCategoriesQuery');
|
|
|
|
const mockedUseSuggestedCorrectionsQuery = vi.mocked(useSuggestedCorrectionsQuery);
|
|
const mockedUseMasterItemsQuery = vi.mocked(useMasterItemsQuery);
|
|
const mockedUseCategoriesQuery = vi.mocked(useCategoriesQuery);
|
|
|
|
// Mock the child CorrectionRow component to isolate the test to the page itself
|
|
vi.mock('./components/CorrectionRow', async () => {
|
|
const { MockCorrectionRow } = await import('../../tests/utils/componentMocks');
|
|
return { CorrectionRow: MockCorrectionRow };
|
|
});
|
|
|
|
// Helper to render the component within router and query contexts
|
|
const renderWithRouter = () => {
|
|
return render(
|
|
<QueryWrapper>
|
|
<MemoryRouter>
|
|
<CorrectionsPage />
|
|
</MemoryRouter>
|
|
</QueryWrapper>,
|
|
);
|
|
};
|
|
|
|
describe('CorrectionsPage', () => {
|
|
const mockCorrections: SuggestedCorrection[] = [
|
|
createMockSuggestedCorrection({
|
|
suggested_correction_id: 1,
|
|
flyer_item_id: 101,
|
|
user_id: 'user-1',
|
|
correction_type: 'item_name',
|
|
suggested_value: 'Organic Bananas',
|
|
flyer_item_name: 'Bananas',
|
|
user_email: 'test@example.com',
|
|
}),
|
|
createMockSuggestedCorrection({
|
|
suggested_correction_id: 2,
|
|
flyer_item_id: 102,
|
|
user_id: 'user-2',
|
|
correction_type: 'price_in_cents',
|
|
suggested_value: '199',
|
|
flyer_item_name: 'Apples',
|
|
user_email: 'test2@example.com',
|
|
}),
|
|
];
|
|
const mockMasterItems: MasterGroceryItem[] = [
|
|
createMockMasterGroceryItem({
|
|
master_grocery_item_id: 1,
|
|
name: 'Organic Bananas',
|
|
category_id: 1,
|
|
category_name: 'Produce',
|
|
}),
|
|
];
|
|
const mockCategories: Category[] = [createMockCategory({ category_id: 1, name: 'Produce' })];
|
|
const mockRefetch = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
// Default mock implementations for the hooks
|
|
mockedUseSuggestedCorrectionsQuery.mockReturnValue({
|
|
data: [],
|
|
isLoading: false,
|
|
error: null,
|
|
refetch: mockRefetch,
|
|
} as any);
|
|
mockedUseMasterItemsQuery.mockReturnValue({
|
|
data: [],
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
mockedUseCategoriesQuery.mockReturnValue({
|
|
data: [],
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
});
|
|
|
|
it('should render a loading spinner while fetching data', async () => {
|
|
mockedUseSuggestedCorrectionsQuery.mockReturnValue({
|
|
data: undefined,
|
|
isLoading: true,
|
|
error: null,
|
|
refetch: mockRefetch,
|
|
} as any);
|
|
|
|
renderWithRouter();
|
|
|
|
expect(screen.getByRole('status', { name: /loading/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display corrections when data is fetched successfully', async () => {
|
|
mockedUseSuggestedCorrectionsQuery.mockReturnValue({
|
|
data: mockCorrections,
|
|
isLoading: false,
|
|
error: null,
|
|
refetch: mockRefetch,
|
|
} as any);
|
|
mockedUseMasterItemsQuery.mockReturnValue({
|
|
data: mockMasterItems,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
mockedUseCategoriesQuery.mockReturnValue({
|
|
data: mockCategories,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
|
|
renderWithRouter();
|
|
|
|
// Check for the mocked CorrectionRow components
|
|
expect(screen.getByTestId('correction-row-1')).toBeInTheDocument();
|
|
expect(screen.getByTestId('correction-row-2')).toBeInTheDocument();
|
|
// Check for the text content within the mocked rows
|
|
expect(screen.getByText('Bananas')).toBeInTheDocument();
|
|
expect(screen.getByText('Apples')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display a message when there are no pending corrections', async () => {
|
|
mockedUseSuggestedCorrectionsQuery.mockReturnValue({
|
|
data: [],
|
|
isLoading: false,
|
|
error: null,
|
|
refetch: mockRefetch,
|
|
} as any);
|
|
mockedUseMasterItemsQuery.mockReturnValue({
|
|
data: mockMasterItems,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
mockedUseCategoriesQuery.mockReturnValue({
|
|
data: mockCategories,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
|
|
renderWithRouter();
|
|
|
|
expect(screen.getByText(/no pending corrections. great job!/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display an error message if fetching corrections fails', async () => {
|
|
const errorMessage = 'Network Error: Failed to fetch';
|
|
mockedUseSuggestedCorrectionsQuery.mockReturnValue({
|
|
data: undefined,
|
|
isLoading: false,
|
|
error: new Error(errorMessage),
|
|
refetch: mockRefetch,
|
|
} as any);
|
|
mockedUseMasterItemsQuery.mockReturnValue({
|
|
data: mockMasterItems,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
mockedUseCategoriesQuery.mockReturnValue({
|
|
data: mockCategories,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
|
|
renderWithRouter();
|
|
|
|
expect(screen.getByText(errorMessage)).toBeInTheDocument();
|
|
});
|
|
|
|
it('should call refetch when the refresh button is clicked', async () => {
|
|
mockedUseSuggestedCorrectionsQuery.mockReturnValue({
|
|
data: mockCorrections,
|
|
isLoading: false,
|
|
error: null,
|
|
refetch: mockRefetch,
|
|
} as any);
|
|
mockedUseMasterItemsQuery.mockReturnValue({
|
|
data: mockMasterItems,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
mockedUseCategoriesQuery.mockReturnValue({
|
|
data: mockCategories,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
|
|
renderWithRouter();
|
|
expect(screen.getByText('Bananas')).toBeInTheDocument();
|
|
|
|
// Click refresh
|
|
const refreshButton = screen.getByTitle('Refresh Corrections');
|
|
fireEvent.click(refreshButton);
|
|
|
|
expect(mockRefetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should call onProcessed callback when a correction is processed', async () => {
|
|
mockedUseSuggestedCorrectionsQuery.mockReturnValue({
|
|
data: mockCorrections,
|
|
isLoading: false,
|
|
error: null,
|
|
refetch: mockRefetch,
|
|
} as any);
|
|
mockedUseMasterItemsQuery.mockReturnValue({
|
|
data: mockMasterItems,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
mockedUseCategoriesQuery.mockReturnValue({
|
|
data: mockCategories,
|
|
isLoading: false,
|
|
error: null,
|
|
} as any);
|
|
|
|
renderWithRouter();
|
|
expect(screen.getByTestId('correction-row-1')).toBeInTheDocument();
|
|
|
|
// Click the process button in the mock row for ID 1
|
|
fireEvent.click(screen.getByTestId('process-btn-1'));
|
|
|
|
// The onProcessed callback should trigger a refetch
|
|
expect(mockRefetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|