Repository navigation
Expand file tree
/
Copy pathvitest.setup.tsx
More file actions
194 lines (185 loc) · 5.58 KB
/
Copy pathvitest.setup.tsx
File metadata and controls
194 lines (185 loc) · 5.58 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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import { vi } from 'vitest';
import '@testing-library/jest-dom/vitest';
// Use vi.hoisted to ensure mock state is available before vi.mock runs
const hoisted = vi.hoisted(() => {
const navigate = vi.fn();
const back = vi.fn();
const forward = vi.fn();
const go = vi.fn();
const state = {
pathname: '/',
params: {} as Record<string, string | undefined>,
navigate,
back,
forward,
go,
};
return {
routerMockState: state,
setMockPathname: (pathname: string) => {
state.pathname = pathname;
},
setMockParams: (params: Record<string, string | undefined>) => {
state.params = params;
},
getMockNavigate: () => state.navigate,
getMockBack: () => state.back,
resetRouterMocks: () => {
state.navigate.mockClear();
state.back.mockClear();
state.forward.mockClear();
state.go.mockClear();
state.pathname = '/';
state.params = {};
},
};
});
// Export the hoisted values
export const routerMockState = hoisted.routerMockState;
export const setMockPathname = hoisted.setMockPathname;
export const setMockParams = hoisted.setMockParams;
export const getMockNavigate = hoisted.getMockNavigate;
export const resetRouterMocks = hoisted.resetRouterMocks;
// Mock TanStack Router hooks globally but pass through route creation functions
// This allows tests using real routers (test-router.tsx) to work while providing
// default mocks for hooks in tests that don't set up their own router context.
vi.mock('@tanstack/react-router', async (importOriginal) => {
const original =
await importOriginal<typeof import('@tanstack/react-router')>();
return {
...original,
// Mock hooks with sensible defaults
useNavigate: () => hoisted.routerMockState.navigate,
useLocation: () => ({
pathname: hoisted.routerMockState.pathname,
search: '',
hash: '',
state: {},
href: hoisted.routerMockState.pathname,
}),
useParams: () => hoisted.routerMockState.params,
useRouter: () => ({
navigate: hoisted.routerMockState.navigate,
history: {
push: hoisted.routerMockState.navigate,
replace: hoisted.routerMockState.navigate,
go: hoisted.routerMockState.go,
back: hoisted.routerMockState.back,
forward: hoisted.routerMockState.forward,
},
state: {
location: {
pathname: hoisted.routerMockState.pathname,
search: '',
hash: '',
},
},
}),
useMatch: () => ({
params: hoisted.routerMockState.params,
pathname: hoisted.routerMockState.pathname,
}),
useSearch: () => ({}),
useRouterState: () => ({
location: {
pathname: hoisted.routerMockState.pathname,
search: '',
hash: '',
},
}),
// Pass through the Link component with a simple mock that preserves navigation
Link: ({
children,
to,
...props
}: {
children: React.ReactNode;
to: string;
[key: string]: unknown;
}) => {
const { onClick, ...rest } = props as {
onClick?: () => void;
[key: string]: unknown;
};
return (
<a
href={to}
onClick={(e) => {
e.preventDefault();
hoisted.routerMockState.navigate({ to });
onClick?.();
}}
{...rest}
>
{children}
</a>
);
},
// Keep the original implementations for router creation (needed by test-router.tsx)
createFileRoute: original.createFileRoute,
createRootRoute: original.createRootRoute,
createRoute: original.createRoute,
createRouter: original.createRouter,
createMemoryHistory: original.createMemoryHistory,
RouterProvider: original.RouterProvider,
Outlet: original.Outlet,
};
});
// Mock window.localStorage
Object.defineProperty(window, 'localStorage', {
value: {
getItem: vi.fn(),
setItem: vi.fn(),
removeItem: vi.fn(),
clear: vi.fn(),
__proto__: {
setItem: vi.fn(),
getItem: vi.fn(),
removeItem: vi.fn(),
clear: vi.fn(),
},
},
writable: true,
});
// Ensure process.nextTick exists without replacing the whole process object
// Some environments (jsdom) may not provide a full Node `process` impl.
// We only define `nextTick` if missing to avoid breaking methods like `emit`.
if (typeof process !== 'undefined' && typeof process.nextTick !== 'function') {
process.nextTick = (fn: () => void): void => {
setTimeout(fn, 0);
};
}
// Mock ResizeObserver for components that use it (e.g., Radix UI)
global.ResizeObserver = class ResizeObserver {
observe() {
// Mock implementation
}
unobserve() {
// Mock implementation
}
disconnect() {
// Mock implementation
}
};
// jsdom has no scrollIntoView, which cmdk calls as its list highlights an item
if (!Element.prototype.scrollIntoView) {
Element.prototype.scrollIntoView = (): void => {
// Mock implementation
};
}
// Soften noisy React warnings that don't affect assertions in our suite.
// These warnings can flood output and cause OOM in CI when many async state
// updates happen during provider effects.
const originalConsoleError = console.error;
console.error = (...args: unknown[]): void => {
const [first] = args as [unknown];
if (
typeof first === 'string' &&
(first.includes('not wrapped in act(...)') ||
first.includes('Each child in a list should have a unique "key" prop') ||
first.includes('useRouter must be used inside a <RouterProvider>'))
) {
return; // ignore these specific, noisy warnings during tests
}
originalConsoleError(...args);
};