All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 15m20s
179 lines
5.1 KiB
TypeScript
179 lines
5.1 KiB
TypeScript
// src/pages/admin/FlyerReviewPage.test.tsx
|
|
import { render, screen, waitFor, within } from '@testing-library/react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { FlyerReviewPage } from './FlyerReviewPage';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import * as apiClient from '../../services/apiClient';
|
|
import { logger } from '../../services/logger.client';
|
|
|
|
// Mock dependencies
|
|
vi.mock('../../services/apiClient', () => ({
|
|
getFlyersForReview: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../services/logger.client', () => ({
|
|
logger: {
|
|
error: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
// Mock LoadingSpinner to simplify DOM and avoid potential issues
|
|
vi.mock('../../components/LoadingSpinner', () => ({
|
|
LoadingSpinner: () => <div data-testid="loading-spinner">Loading...</div>,
|
|
}));
|
|
|
|
describe('FlyerReviewPage', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('renders loading spinner initially', () => {
|
|
// Mock a promise that doesn't resolve immediately to check loading state
|
|
vi.mocked(apiClient.getFlyersForReview).mockReturnValue(new Promise(() => {}));
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<FlyerReviewPage />
|
|
</MemoryRouter>
|
|
);
|
|
|
|
expect(screen.getByRole('status', { name: /loading flyers for review/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders empty state when no flyers are returned', async () => {
|
|
vi.mocked(apiClient.getFlyersForReview).mockResolvedValue({
|
|
ok: true,
|
|
json: async () => [],
|
|
} as Response);
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<FlyerReviewPage />
|
|
</MemoryRouter>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
|
});
|
|
|
|
expect(screen.getByText(/the review queue is empty/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders a list of flyers when API returns data', async () => {
|
|
const mockFlyers = [
|
|
{
|
|
flyer_id: 1,
|
|
file_name: 'flyer1.jpg',
|
|
created_at: '2023-01-01T00:00:00Z',
|
|
store: { name: 'Store A' },
|
|
icon_url: 'icon1.jpg',
|
|
},
|
|
{
|
|
flyer_id: 2,
|
|
file_name: 'flyer2.jpg',
|
|
created_at: '2023-01-02T00:00:00Z',
|
|
store: { name: 'Store B' },
|
|
icon_url: 'icon2.jpg',
|
|
},
|
|
{
|
|
flyer_id: 3,
|
|
file_name: 'flyer3.jpg',
|
|
created_at: '2023-01-03T00:00:00Z',
|
|
store: null,
|
|
icon_url: null,
|
|
},
|
|
];
|
|
|
|
vi.mocked(apiClient.getFlyersForReview).mockResolvedValue({
|
|
ok: true,
|
|
json: async () => mockFlyers,
|
|
} as Response);
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<FlyerReviewPage />
|
|
</MemoryRouter>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
|
});
|
|
|
|
expect(screen.getByText('Store A')).toBeInTheDocument();
|
|
expect(screen.getByText('flyer1.jpg')).toBeInTheDocument();
|
|
expect(screen.getByText('Store B')).toBeInTheDocument();
|
|
expect(screen.getByText('flyer2.jpg')).toBeInTheDocument();
|
|
|
|
// Test fallback for null store and icon_url
|
|
expect(screen.getByText('Unknown Store')).toBeInTheDocument();
|
|
expect(screen.getByText('flyer3.jpg')).toBeInTheDocument();
|
|
const unknownStoreItem = screen.getByText('Unknown Store').closest('li');
|
|
const unknownStoreImage = within(unknownStoreItem!).getByRole('img');
|
|
expect(unknownStoreImage).not.toHaveAttribute('src');
|
|
expect(unknownStoreImage).not.toHaveAttribute('alt');
|
|
});
|
|
|
|
it('renders error message when API response is not ok', async () => {
|
|
vi.mocked(apiClient.getFlyersForReview).mockResolvedValue({
|
|
ok: false,
|
|
json: async () => ({ message: 'Server error' }),
|
|
} as Response);
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<FlyerReviewPage />
|
|
</MemoryRouter>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
|
});
|
|
|
|
expect(screen.getByText('Server error')).toBeInTheDocument();
|
|
expect(logger.error).toHaveBeenCalledWith(
|
|
expect.objectContaining({ err: expect.any(Error) }),
|
|
'Failed to fetch flyers for review'
|
|
);
|
|
});
|
|
|
|
it('renders error message when API throws an error', async () => {
|
|
const networkError = new Error('Network error');
|
|
vi.mocked(apiClient.getFlyersForReview).mockRejectedValue(networkError);
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<FlyerReviewPage />
|
|
</MemoryRouter>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
|
});
|
|
|
|
expect(screen.getByText('Network error')).toBeInTheDocument();
|
|
expect(logger.error).toHaveBeenCalledWith(
|
|
{ err: networkError },
|
|
'Failed to fetch flyers for review'
|
|
);
|
|
});
|
|
|
|
it('renders a generic error for non-Error rejections', async () => {
|
|
const nonErrorRejection = { message: 'This is not an Error object' };
|
|
vi.mocked(apiClient.getFlyersForReview).mockRejectedValue(nonErrorRejection);
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<FlyerReviewPage />
|
|
</MemoryRouter>,
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('An unknown error occurred while fetching data.')).toBeInTheDocument();
|
|
});
|
|
|
|
expect(logger.error).toHaveBeenCalledWith(
|
|
{ err: nonErrorRejection },
|
|
'Failed to fetch flyers for review',
|
|
);
|
|
});
|
|
}); |