Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { render, screen} from '@testing-library/react'; | |
| import userEvent from '@testing-library/user-event'; | |
| import { vi, describe, test, beforeEach, expect } from 'vitest'; | |
| import { BrowserRouter } from 'react-router-dom'; | |
| import { FilterProvider } from '../../contexts/FilterContext'; | |
| import { AdminProvider } from '../../contexts/AdminContext'; | |
| import HeaderNav from '../../components/HeaderNav'; | |
| import FilterBar from '../../components/FilterBar'; | |
| import ExportModal from '../../components/ExportModal'; | |
| import HelpPage from '../../pages/HelpPage'; | |
| // Mock react-router-dom | |
| const mockNavigate = vi.fn(); | |
| const mockUseLocation = vi.fn(); | |
| vi.mock('react-router-dom', () => ({ | |
| BrowserRouter: ({ children }: { children: React.ReactNode }) => <>{children}</>, | |
| useNavigate: () => mockNavigate, | |
| useLocation: () => mockUseLocation(), | |
| })); | |
| // Mock the useFilterContext hook | |
| const mockUseFilterContext = { | |
| search: '', | |
| srcFilter: '', | |
| catFilter: '', | |
| regionFilter: '', | |
| countryFilter: '', | |
| imageTypeFilter: '', | |
| showReferenceExamples: false, | |
| setSearch: vi.fn(), | |
| setSrcFilter: vi.fn(), | |
| setCatFilter: vi.fn(), | |
| setRegionFilter: vi.fn(), | |
| setCountryFilter: vi.fn(), | |
| setImageTypeFilter: vi.fn(), | |
| setShowReferenceExamples: vi.fn(), | |
| clearAllFilters: vi.fn(), | |
| }; | |
| // Mock the useAdminContext hook | |
| const mockUseAdminContext = { | |
| isAdmin: true, | |
| login: vi.fn(), | |
| logout: vi.fn(), | |
| }; | |
| vi.mock('../../hooks/useFilterContext', () => ({ | |
| useFilterContext: () => mockUseFilterContext, | |
| })); | |
| vi.mock('../../hooks/useAdminContext', () => ({ | |
| useAdminContext: () => mockUseAdminContext, | |
| })); | |
| // Mock JSZip | |
| vi.mock('jszip', () => ({ | |
| __esModule: true, | |
| default: vi.fn().mockImplementation(() => ({ | |
| file: vi.fn(), | |
| generateAsync: vi.fn().mockResolvedValue('mock-zip-data'), | |
| })), | |
| })); | |
| describe('App Workflow Integration', () => { | |
| const mockProps = { | |
| sources: [{ s_code: 'WFP', label: 'World Food Programme' }, { s_code: 'IFRC', label: 'IFRC' }], | |
| types: [{ t_code: 'EARTHQUAKE', label: 'Earthquake' }, { t_code: 'FLOOD', label: 'Flood' }], | |
| regions: [{ r_code: 'ASIA', label: 'Asia' }, { r_code: 'AFRICA', label: 'Africa' }], | |
| countries: [{ c_code: 'BD', label: 'Bangladesh', r_code: 'ASIA' }, { c_code: 'IN', label: 'India', r_code: 'ASIA' }], | |
| imageTypes: [{ image_type: 'SATELLITE', label: 'Satellite' }, { image_type: 'AERIAL', label: 'Aerial' }], | |
| }; | |
| beforeEach(() => { | |
| vi.clearAllMocks(); | |
| mockUseLocation.mockReturnValue({ pathname: '/' }); | |
| }); | |
| test('Complete user workflow: navigate, filter, and export', async () => { | |
| const user = userEvent.setup(); | |
| const mockOnClose = vi.fn(); | |
| const mockOnExport = vi.fn(); | |
| render( | |
| <BrowserRouter> | |
| <FilterProvider> | |
| <AdminProvider> | |
| <HeaderNav /> | |
| <FilterBar {...mockProps} /> | |
| <ExportModal | |
| isOpen={true} | |
| onClose={mockOnClose} | |
| onExport={mockOnExport} | |
| filteredCount={2} | |
| totalCount={10} | |
| hasFilters={true} | |
| crisisMapsCount={1} | |
| droneImagesCount={1} | |
| variant="bulk" | |
| /> | |
| </AdminProvider> | |
| </FilterProvider> | |
| </BrowserRouter> | |
| ); | |
| // Step 1: Navigate to help page | |
| const helpButton = screen.getByRole('button', { name: /help/i }); | |
| await user.click(helpButton); | |
| expect(mockNavigate).toHaveBeenCalledWith('/help'); | |
| // Step 2: Apply filters | |
| const sourceInput = screen.getByPlaceholderText('All Sources'); | |
| const categoryInput = screen.getByPlaceholderText('All Categories'); | |
| await user.click(sourceInput); | |
| const wfpOption = screen.getByText('World Food Programme'); | |
| await user.click(wfpOption); | |
| expect(mockUseFilterContext.setSrcFilter).toHaveBeenCalledWith('WFP'); | |
| await user.click(categoryInput); | |
| const earthquakeOption = screen.getByText('Earthquake'); | |
| await user.click(earthquakeOption); | |
| expect(mockUseFilterContext.setCatFilter).toHaveBeenCalledWith('EARTHQUAKE'); | |
| // Step 3: Check export modal | |
| expect(screen.getByText(/Crisis Maps/i)).toBeInTheDocument(); | |
| expect(screen.getByText(/Drone Images/i)).toBeInTheDocument(); | |
| expect(screen.getByRole('button', { name: /export selected/i })).toBeInTheDocument(); | |
| }); | |
| test('Admin workflow: access admin features and manage data', async () => { | |
| // Mock admin state | |
| mockUseAdminContext.isAdmin = true; | |
| render( | |
| <BrowserRouter> | |
| <FilterProvider> | |
| <AdminProvider> | |
| <HeaderNav /> | |
| <ExportModal | |
| isOpen={true} | |
| onClose={vi.fn()} | |
| onExport={vi.fn()} | |
| filteredCount={1} | |
| totalCount={10} | |
| hasFilters={true} | |
| crisisMapsCount={1} | |
| droneImagesCount={0} | |
| variant="bulk" | |
| /> | |
| </AdminProvider> | |
| </FilterProvider> | |
| </BrowserRouter> | |
| ); | |
| // Step 1: Check admin navigation | |
| const adminButton = screen.getByRole('button', { name: /dev/i }); | |
| expect(adminButton).toBeInTheDocument(); | |
| // Step 2: Check admin export features | |
| expect(screen.getByText(/Export Dataset/i)).toBeInTheDocument(); | |
| expect(screen.getByText(/Crisis Maps/i)).toBeInTheDocument(); | |
| expect(screen.getByText(/Drone Images/i)).toBeInTheDocument(); | |
| }); | |
| test('Filter workflow: apply and clear filters', async () => { | |
| const user = userEvent.setup(); | |
| render( | |
| <BrowserRouter> | |
| <FilterProvider> | |
| <AdminProvider> | |
| <FilterBar {...mockProps} /> | |
| </AdminProvider> | |
| </FilterProvider> | |
| </BrowserRouter> | |
| ); | |
| // Step 1: Apply multiple filters | |
| const sourceInput = screen.getByPlaceholderText('All Sources'); | |
| const categoryInput = screen.getByPlaceholderText('All Categories'); | |
| await user.click(sourceInput); | |
| const ifrcOption = screen.getByText('IFRC'); | |
| await user.click(ifrcOption); | |
| await user.click(categoryInput); | |
| const floodOption = screen.getByText('Flood'); | |
| await user.click(floodOption); | |
| // Step 2: Verify filters are set | |
| expect(mockUseFilterContext.setSrcFilter).toHaveBeenCalledWith('IFRC'); | |
| expect(mockUseFilterContext.setCatFilter).toHaveBeenCalledWith('FLOOD'); | |
| // Step 3: Clear all filters | |
| const clearButton = screen.getByRole('button', { name: /clear/i }); | |
| await user.click(clearButton); | |
| expect(mockUseFilterContext.clearAllFilters).toHaveBeenCalled(); | |
| }); | |
| test('Navigation workflow: move between different pages', async () => { | |
| const user = userEvent.setup(); | |
| render( | |
| <BrowserRouter> | |
| <FilterProvider> | |
| <AdminProvider> | |
| <HeaderNav /> | |
| <HelpPage /> | |
| </AdminProvider> | |
| </FilterProvider> | |
| </BrowserRouter> | |
| ); | |
| // Step 1: Navigate to help page | |
| const helpButton = screen.getByRole('button', { name: /help/i }); | |
| await user.click(helpButton); | |
| expect(mockNavigate).toHaveBeenCalledWith('/help'); | |
| // Step 2: Check that help page is rendered | |
| expect(screen.getByRole('heading', { name: /Introduction/i })).toBeInTheDocument(); | |
| }); | |
| test('Context integration: filters and admin state work together', () => { | |
| // Mock admin state | |
| mockUseAdminContext.isAdmin = true; | |
| render( | |
| <BrowserRouter> | |
| <FilterProvider> | |
| <AdminProvider> | |
| <HeaderNav /> | |
| <FilterBar {...mockProps} /> | |
| <ExportModal | |
| isOpen={true} | |
| onClose={vi.fn()} | |
| onExport={vi.fn()} | |
| filteredCount={1} | |
| totalCount={10} | |
| hasFilters={true} | |
| crisisMapsCount={1} | |
| droneImagesCount={0} | |
| variant="bulk" | |
| /> | |
| </AdminProvider> | |
| </FilterProvider> | |
| </BrowserRouter> | |
| ); | |
| // Check that admin features are available | |
| expect(screen.getByRole('button', { name: /dev/i })).toBeInTheDocument(); | |
| expect(screen.getByText(/Export Dataset/i)).toBeInTheDocument(); | |
| // Check that filter functionality is available | |
| expect(screen.getByPlaceholderText('All Sources')).toBeInTheDocument(); | |
| expect(screen.getByPlaceholderText('All Categories')).toBeInTheDocument(); | |
| }); | |
| test('Error handling workflow: handle missing data gracefully', () => { | |
| render( | |
| <BrowserRouter> | |
| <FilterProvider> | |
| <AdminProvider> | |
| <ExportModal | |
| isOpen={true} | |
| onClose={vi.fn()} | |
| onExport={vi.fn()} | |
| filteredCount={0} | |
| totalCount={10} | |
| hasFilters={true} | |
| crisisMapsCount={0} | |
| droneImagesCount={0} | |
| variant="bulk" | |
| /> | |
| </AdminProvider> | |
| </FilterProvider> | |
| </BrowserRouter> | |
| ); | |
| // Check that empty state is handled gracefully | |
| expect(screen.getByText(/Crisis Maps \(0 images\)/i)).toBeInTheDocument(); | |
| expect(screen.getByText(/Drone Images \(0 images\)/i)).toBeInTheDocument(); | |
| expect(screen.getByRole('button', { name: /export selected/i })).toBeInTheDocument(); | |
| }); | |
| }); | |