-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvitest.setup.jsx
More file actions
108 lines (101 loc) · 3.29 KB
/
vitest.setup.jsx
File metadata and controls
108 lines (101 loc) · 3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import * as matchers from '@testing-library/jest-dom/matchers';
import { cleanup } from '@testing-library/react';
import { afterEach, expect, vi } from 'vitest';
expect.extend(matchers);
/**
* Global after-each teardown.
*
* Runs automatically after every test to prevent state leaking between cases:
* - `cleanup()` unmounts any React trees rendered with `@testing-library/react`
* - `vi.clearAllMocks()` resets call counts and return values on all mocks
* - `vi.unstubAllGlobals()` restores any globals stubbed with `vi.stubGlobal()`
*/
afterEach(() => {
cleanup();
vi.clearAllMocks();
vi.unstubAllGlobals();
});
/**
* Lucide React icon mock.
*
* Replaces every named icon export with a lightweight `<div>` stub that carries
* a `data-testid="icon-<IconName>"` attribute. This keeps snapshots readable
* and avoids rendering full SVG markup in tests.
*
* @example
* expect(screen.getByTestId('icon-ChevronDown')).toBeInTheDocument();
*/
vi.mock('lucide-react', async () => {
const actual = await vi.importActual('lucide-react');
return Object.keys(actual).reduce((acc, curr) => {
acc[curr] = () => <div data-testid={`icon-${curr}`} />;
return acc;
}, {});
});
/**
* Browser API mocks for the jsdom environment.
*
* jsdom does not fully implement browser APIs. This block shims the ones most
* commonly used in the app so tests can interact with them without errors.
*
* Mocked APIs:
* - `window.localStorage` — in-memory key/value store; resets between tests
* via `vi.clearAllMocks()` in the afterEach above
* - `window.location` — stubs `reload`, `assign`, and `replace` so navigation
* calls in components can be asserted with `expect(...).toHaveBeenCalled()`
*/
if (typeof window !== 'undefined') {
let store = {};
Object.defineProperty(window, 'localStorage', {
value: {
/** @param {string} key */
getItem: (key) => store[key] || null,
/**
* @param {string} key
* @param {string} value
*/
setItem: (key, value) => {
store[key] = String(value);
},
/** @param {string} key */
removeItem: (key) => {
delete store[key];
},
clear: () => {
store = {};
},
},
writable: true,
});
Object.defineProperty(window, 'location', {
value: { reload: vi.fn(), assign: vi.fn(), replace: vi.fn() },
writable: true,
});
}
/**
* React Router mock.
*
* Spreads the real `react-router` module and overrides only the hooks and
* components that trigger navigation side-effects during tests:
*
* - `useNavigate` — returns a fresh `vi.fn()` so push/replace calls can be
* asserted without an actual router context
* - `useRouteError` — returns `undefined` by default; override per-test with
* `vi.mocked(useRouteError).mockReturnValue(new Error('...'))`
* - `Navigate` — renders nothing, preventing redirects from crashing tests
* that render components outside a full router tree
*
* @example
* const navigate = vi.fn();
* vi.mocked(useNavigate).mockReturnValue(navigate);
* expect(navigate).toHaveBeenCalledWith('/dashboard');
*/
vi.mock('react-router', async () => {
const actual = await vi.importActual('react-router');
return {
...actual,
useNavigate: vi.fn(() => vi.fn()),
useRouteError: vi.fn(),
Navigate: vi.fn(() => null),
};
});