feat: add MSW for integration test mocking
Install Mock Service Worker (MSW) and configure it for vitest. Write one integration test for usePurchases hook using MSW. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
committed by
Barcode Betty
parent
d7cfc00a8d
commit
cf26dd3049
Generated
+680
-462
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
"globals": "^17.4.0",
|
"globals": "^17.4.0",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^25.0.1",
|
||||||
|
"msw": "^2.12.14",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.56.1",
|
"typescript-eslint": "^8.56.1",
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { renderHook, waitFor } from '@testing-library/react'
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
import { usePurchases } from '../useApi'
|
||||||
|
import { http, HttpResponse } from 'msw'
|
||||||
|
import { server } from '../../test/mocks/server'
|
||||||
|
|
||||||
|
function createWrapper() {
|
||||||
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: { queries: { retry: false } },
|
||||||
|
})
|
||||||
|
return function Wrapper({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{children}
|
||||||
|
</QueryClientProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('useApi hooks', () => {
|
||||||
|
describe('usePurchases', () => {
|
||||||
|
it('fetches and returns purchases', async () => {
|
||||||
|
const { result } = renderHook(() => usePurchases(), { wrapper: createWrapper() })
|
||||||
|
|
||||||
|
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||||
|
|
||||||
|
expect(result.current.data).toHaveLength(1)
|
||||||
|
expect(result.current.data![0]).toMatchObject({
|
||||||
|
id: 'pur_1',
|
||||||
|
storeName: 'Kroger',
|
||||||
|
total: 42.5,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an error when the endpoint fails', async () => {
|
||||||
|
server.use(
|
||||||
|
http.get('/api/v1/purchases', () => HttpResponse.error()),
|
||||||
|
)
|
||||||
|
|
||||||
|
const { result } = renderHook(() => usePurchases(), { wrapper: createWrapper() })
|
||||||
|
|
||||||
|
await waitFor(() => expect(result.current.isError).toBe(true))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { http, HttpResponse } from 'msw'
|
||||||
|
import type { Purchase, Product, Coupon, PriceAlert } from '../../types/api.ts'
|
||||||
|
|
||||||
|
const mockPurchases: Purchase[] = [
|
||||||
|
{
|
||||||
|
id: 'pur_1',
|
||||||
|
storeId: 'store_1',
|
||||||
|
storeName: 'Kroger',
|
||||||
|
date: '2024-01-15',
|
||||||
|
total: 42.5,
|
||||||
|
items: [
|
||||||
|
{ id: 'item_1', productId: 'prod_1', name: 'Milk', quantity: 1, price: 3.99, unitPrice: 3.99 },
|
||||||
|
{ id: 'item_2', productId: 'prod_2', name: 'Bread', quantity: 2, price: 5.98, unitPrice: 2.99 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const mockProducts: Product[] = [
|
||||||
|
{
|
||||||
|
id: 'prod_1',
|
||||||
|
name: 'Whole Milk',
|
||||||
|
brand: 'Kroger',
|
||||||
|
category: 'Dairy',
|
||||||
|
prices: [{ storeId: 'store_1', storeName: 'Kroger', price: 3.99, lastUpdated: '2024-01-15' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prod_2',
|
||||||
|
name: 'Whole Wheat Bread',
|
||||||
|
brand: 'Nature\'s Own',
|
||||||
|
category: 'Bakery',
|
||||||
|
prices: [{ storeId: 'store_1', storeName: 'Kroger', price: 2.99, lastUpdated: '2024-01-15' }],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const mockCoupons: Coupon[] = [
|
||||||
|
{
|
||||||
|
id: 'coupon_1',
|
||||||
|
productId: 'prod_1',
|
||||||
|
storeName: 'Kroger',
|
||||||
|
description: '$1 off milk',
|
||||||
|
discount: '$1.00',
|
||||||
|
expiresAt: '2024-12-31',
|
||||||
|
code: 'MILK1',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const mockAlerts: PriceAlert[] = [
|
||||||
|
{
|
||||||
|
id: 'alert_1',
|
||||||
|
productId: 'prod_1',
|
||||||
|
productName: 'Whole Milk',
|
||||||
|
targetPrice: 2.99,
|
||||||
|
currentPrice: 3.99,
|
||||||
|
triggered: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const handlers = [
|
||||||
|
http.get('/api/v1/health', () => HttpResponse.json({ status: 'ok' })),
|
||||||
|
http.get('/api/v1/purchases', () => HttpResponse.json(mockPurchases)),
|
||||||
|
http.get('/api/v1/products', () => HttpResponse.json(mockProducts)),
|
||||||
|
http.get('/api/v1/products/prod_1', () => HttpResponse.json(mockProducts[0])),
|
||||||
|
http.get('/api/v1/coupons', () => HttpResponse.json(mockCoupons)),
|
||||||
|
http.get('/api/v1/price-alerts', () => HttpResponse.json(mockAlerts)),
|
||||||
|
]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { setupServer } from 'msw/node'
|
||||||
|
import { handlers } from './handlers'
|
||||||
|
|
||||||
|
export const server = setupServer(...handlers)
|
||||||
@@ -1 +1,6 @@
|
|||||||
import '@testing-library/jest-dom/vitest'
|
import '@testing-library/jest-dom/vitest'
|
||||||
|
import { server } from './mocks/server'
|
||||||
|
|
||||||
|
beforeAll(() => server.listen())
|
||||||
|
afterEach(() => server.resetHandlers())
|
||||||
|
afterAll(() => server.close())
|
||||||
|
|||||||
Reference in New Issue
Block a user