Created
November 2, 2024 06:27
-
-
Save truongluu/c12852dc6fdc357b7b70f34af2ee78e5 to your computer and use it in GitHub Desktop.
Memory Router Testing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/test/test-utils.tsx | |
import { render } from '@testing-library/react' | |
import { MemoryRouter, Routes, Route } from 'react-router-dom' | |
// Enhanced render utility that accepts route configuration | |
interface RenderWithRouterOptions { | |
route?: string | |
initialEntries?: string[] | |
initialIndex?: number | |
} | |
export function renderWithRouter( | |
ui: React.ReactElement, | |
{ | |
route = '/', | |
initialEntries = [route], | |
initialIndex, | |
...renderOptions | |
}: RenderWithRouterOptions = {} | |
) { | |
return { | |
...render( | |
<MemoryRouter initialEntries={initialEntries} initialIndex={initialIndex}> | |
{ui} | |
</MemoryRouter>, | |
renderOptions | |
), | |
} | |
} | |
// Example components for testing | |
// src/components/Navigation.tsx | |
import { useLocation, useNavigate } from 'react-router-dom' | |
const Navigation = () => { | |
const navigate = useNavigate() | |
const location = useLocation() | |
return ( | |
<nav> | |
<button onClick={() => navigate('/home')}>Home</button> | |
<button onClick={() => navigate('/about')}>About</button> | |
<button onClick={() => navigate(-1)}>Back</button> | |
<div data-testid="location-display"> | |
Current: {location.pathname} | |
</div> | |
</nav> | |
) | |
} | |
// src/components/Navigation.test.tsx | |
import { describe, it, expect } from 'vitest' | |
import { screen, waitFor } from '@testing-library/react' | |
import userEvent from '@testing-library/user-event' | |
import Navigation from './Navigation' | |
describe('Navigation with MemoryRouter', () => { | |
// Test 1: Basic Navigation | |
it('renders at initial route', () => { | |
renderWithRouter(<Navigation />, { | |
initialEntries: ['/home'] | |
}) | |
expect(screen.getByTestId('location-display')).toHaveTextContent('/home') | |
}) | |
// Test 2: Navigation History | |
it('maintains navigation history', async () => { | |
renderWithRouter(<Navigation />, { | |
initialEntries: ['/home', '/about', '/contact'], | |
initialIndex: 1 // Start at '/about' | |
}) | |
expect(screen.getByTestId('location-display')).toHaveTextContent('/about') | |
// Click back button | |
await userEvent.click(screen.getByText('Back')) | |
expect(screen.getByTestId('location-display')).toHaveTextContent('/home') | |
}) | |
// Test 3: Testing Routes with Parameters | |
it('handles route parameters', () => { | |
const ProductPage = () => { | |
const { id } = useParams<{ id: string }>() | |
return <div data-testid="product-id">Product: {id}</div> | |
} | |
render( | |
<MemoryRouter initialEntries={['/product/123']}> | |
<Routes> | |
<Route path="/product/:id" element={<ProductPage />} /> | |
</Routes> | |
</MemoryRouter> | |
) | |
expect(screen.getByTestId('product-id')).toHaveTextContent('Product: 123') | |
}) | |
// Test 4: Testing Query Parameters | |
it('handles query parameters', () => { | |
const SearchPage = () => { | |
const [searchParams] = useSearchParams() | |
return <div data-testid="search-query"> | |
Query: {searchParams.get('q')} | |
</div> | |
} | |
render( | |
<MemoryRouter initialEntries={['/search?q=test-query']}> | |
<Routes> | |
<Route path="/search" element={<SearchPage />} /> | |
</Routes> | |
</MemoryRouter> | |
) | |
expect(screen.getByTestId('search-query')).toHaveTextContent('Query: test-query') | |
}) | |
// Test 5: Testing Protected Routes | |
it('redirects unauthorized access', () => { | |
const ProtectedRoute = () => { | |
const isAuthenticated = false | |
return isAuthenticated ? ( | |
<div>Protected Content</div> | |
) : ( | |
<Navigate to="/login" replace /> | |
) | |
} | |
render( | |
<MemoryRouter initialEntries={['/dashboard']}> | |
<Routes> | |
<Route path="/dashboard" element={<ProtectedRoute />} /> | |
<Route path="/login" element={<div>Login Page</div>} /> | |
</Routes> | |
</MemoryRouter> | |
) | |
expect(screen.getByText('Login Page')).toBeInTheDocument() | |
}) | |
// Test 6: Testing Nested Routes | |
it('handles nested routes', () => { | |
const Layout = () => ( | |
<div> | |
<div>Header</div> | |
<Outlet /> | |
</div> | |
) | |
const NestedPage = () => <div>Nested Content</div> | |
render( | |
<MemoryRouter initialEntries={['/parent/child']}> | |
<Routes> | |
<Route path="/parent" element={<Layout />}> | |
<Route path="child" element={<NestedPage />} /> | |
</Route> | |
</Routes> | |
</MemoryRouter> | |
) | |
expect(screen.getByText('Header')).toBeInTheDocument() | |
expect(screen.getByText('Nested Content')).toBeInTheDocument() | |
}) | |
// Test 7: Testing Route State | |
it('handles location state', () => { | |
const StateComponent = () => { | |
const location = useLocation() | |
return <div data-testid="state-display"> | |
State: {location.state?.message} | |
</div> | |
} | |
render( | |
<MemoryRouter | |
initialEntries={[ | |
{ | |
pathname: '/state-test', | |
state: { message: 'Hello from state!' } | |
} | |
]} | |
> | |
<StateComponent /> | |
</MemoryRouter> | |
) | |
expect(screen.getByTestId('state-display')) | |
.toHaveTextContent('State: Hello from state!') | |
}) | |
// Test 8: Testing Multiple Navigation Steps | |
it('handles multiple navigation steps', async () => { | |
renderWithRouter(<Navigation />, { | |
initialEntries: ['/start'], | |
initialIndex: 0 | |
}) | |
// Navigate forward | |
await userEvent.click(screen.getByText('Home')) | |
expect(screen.getByTestId('location-display')).toHaveTextContent('/home') | |
await userEvent.click(screen.getByText('About')) | |
expect(screen.getByTestId('location-display')).toHaveTextContent('/about') | |
// Navigate backward twice | |
await userEvent.click(screen.getByText('Back')) | |
expect(screen.getByTestId('location-display')).toHaveTextContent('/home') | |
await userEvent.click(screen.getByText('Back')) | |
expect(screen.getByTestId('location-display')).toHaveTextContent('/start') | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment