Skip to content

Commit e839250

Browse files
committed
03/05: assert on href and enabled
1 parent 766dc34 commit e839250

File tree

9 files changed

+347
-37
lines changed

9 files changed

+347
-37
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# Page navigation
22

33
Problem: the component you are testing does navigation to another page. Let's see what exactly you should test here on the integration level and how to do so.
4+
5+
- Showcases how to create a custom _wrapper_ for `<MemoryRouter />`.

exercises/03.best-practices/05.solution.page-navigation/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"msw": "^2.7.0",
2222
"playwright": "^1.49.1",
2323
"postcss": "^8.4.49",
24+
"react-router": "^7.1.3",
2425
"tailwindcss": "^3.4.17",
2526
"vite": "^6.0.7",
2627
"vitest": "^3.0.2"
Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
1-
import { useMemo } from 'react'
1+
import { BrowserRouter, Routes, Route } from 'react-router'
22
import { DiscountCodeForm } from './discount-code-form.js'
33

44
export function App() {
5-
const location = useMemo(() => {
6-
return window.location
7-
}, [])
8-
9-
switch (location.pathname) {
10-
case '/': {
11-
return <DiscountCodeForm />
12-
}
13-
14-
case '/cart': {
15-
return (
16-
<div className="text-center">
17-
<h1 className="mb-2 text-4xl font-bold">Cart</h1>
18-
<p className="text-slate-600">This is a cart page.</p>
19-
</div>
20-
)
21-
}
22-
23-
default: {
24-
return <p>Page not found</p>
25-
}
26-
}
5+
return (
6+
<BrowserRouter>
7+
<Routes>
8+
<Route path="/" element={<DiscountCodeForm />} />
9+
<Route
10+
path="/cart"
11+
element={
12+
<div className="text-center">
13+
<h1 className="mb-2 text-4xl font-bold">Cart</h1>
14+
<p className="text-slate-600">This is a cart page.</p>
15+
</div>
16+
}
17+
/>
18+
<Route path="*" element={<p>Page not found</p>} />
19+
</Routes>
20+
</BrowserRouter>
21+
)
2722
}

exercises/03.best-practices/05.solution.page-navigation/src/discount-code-form.browser.test.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { page } from '@vitest/browser/context'
22
import { render } from 'vitest-browser-react'
33
import { http, HttpResponse } from 'msw'
4+
import { MemoryRouter, useLocation } from 'react-router'
45
import { test } from '../test-extend.js'
56
import { DiscountCodeForm, type Discount } from './discount-code-form.js'
67

8+
const wrapper: React.JSXElementConstructor<{
9+
children: React.ReactNode
10+
}> = ({ children }) => {
11+
return <MemoryRouter>{children}</MemoryRouter>
12+
}
13+
714
test('applies a discount code', async () => {
8-
render(<DiscountCodeForm />)
15+
render(<DiscountCodeForm />, { wrapper })
916

1017
const discountInput = page.getByLabelText('Discount code')
1118
await discountInput.fill('EPIC2025')
@@ -36,7 +43,7 @@ test('displays a warning for legacy discount codes', async ({ worker }) => {
3643
),
3744
)
3845

39-
render(<DiscountCodeForm />)
46+
render(<DiscountCodeForm />, { wrapper })
4047

4148
const discountInput = page.getByLabelText('Discount code')
4249
await discountInput.fill('LEGA2000')
@@ -63,7 +70,7 @@ test('displays an error when fetching the discount fails', async ({
6370
}),
6471
)
6572

66-
render(<DiscountCodeForm />)
73+
render(<DiscountCodeForm />, { wrapper })
6774

6875
const discountInput = page.getByLabelText('Discount code')
6976
await discountInput.fill('CODE1234')
@@ -79,7 +86,7 @@ test('displays an error when fetching the discount fails', async ({
7986
})
8087

8188
test('removes the applied discount code', async () => {
82-
render(<DiscountCodeForm />)
89+
render(<DiscountCodeForm />, { wrapper })
8390

8491
const discountInput = page.getByLabelText('Discount code')
8592
await discountInput.fill('EPIC2025')
@@ -99,3 +106,11 @@ test('removes the applied discount code', async () => {
99106

100107
await expect.element(discountText).not.toBeInTheDocument()
101108
})
109+
110+
test('redirects to the cart page ...', async () => {
111+
render(<DiscountCodeForm />, { wrapper })
112+
113+
const backToCartLink = page.getByRole('link', { name: 'Back to cart' })
114+
await expect.element(backToCartLink).toHaveAttribute('href', '/cart')
115+
await expect.element(backToCartLink).toBeEnabled()
116+
})

exercises/03.best-practices/05.solution.page-navigation/src/discount-code-form.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useReducer, useState, type FormEventHandler } from 'react'
2+
import { Link } from 'react-router'
23

34
interface Notification {
45
type: 'error' | 'warning'
@@ -184,12 +185,12 @@ export function DiscountCodeForm() {
184185
</button>
185186

186187
<p className="text-center">
187-
<a
188-
href="/cart"
188+
<Link
189+
to="/cart"
189190
className="text-sm font-medium text-slate-500 hover:underline"
190191
>
191192
Back to cart
192-
</a>
193+
</Link>
193194
</p>
194195
</form>
195196
)}

exercises/03.best-practices/06.solution.component-wrappers/README.mdx

Lines changed: 0 additions & 3 deletions
This file was deleted.

exercises/03.best-practices/06.solution.component-wrappers/package.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

exercises/04.debugging/README.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# Debugging tests
22

33
Learn how to debug your browser tests in Vitest. Your runbook to follow when a test fails.
4+
5+
- There's `{ debug } = render(element)` that can be used!

0 commit comments

Comments
 (0)