-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvitest.setup.ts
More file actions
146 lines (139 loc) · 5.03 KB
/
Copy pathvitest.setup.ts
File metadata and controls
146 lines (139 loc) · 5.03 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
import "@testing-library/jest-dom/vitest";
import { beforeEach } from "vitest";
/**
* `ResizeObserver` no-op polyfill for the JSDOM-backed component tests
* (`// @vitest-environment jsdom` headers in *.test.tsx files). JSDOM
* doesn't implement it, but Radix UI's `<ScrollArea>` calls
* `new ResizeObserver(...)` on mount as soon as the scrollbar is visible
* — which is the case any time we render the chat panel under
* `type="always"`. Without this stub, every test that mounts ChatPanel
* (or anything else nesting a visible ScrollArea) crashes with
* "ResizeObserver is not defined" before assertions can run.
*
* The stub is intentionally inert: tests that actually need to react to
* size changes should override this with a richer mock at the test
* level. The Convex/edge-runtime tests don't touch the DOM, so the
* shared install here is harmless for them.
*/
class ResizeObserverStub {
observe(): void {}
unobserve(): void {}
disconnect(): void {}
}
// Cast through `unknown` so this file doesn't depend on the DOM lib
// (vitest.setup.ts is type-checked under tsconfig.node.json, which only
// pulls in ES2023). `globalThis.ResizeObserver` exists at runtime in
// JSDOM-or-better environments — we only assign it when missing.
const globalScope = globalThis as unknown as {
ResizeObserver?: unknown;
document?: unknown;
window?: {
localStorage?: {
clear?: () => void;
};
sessionStorage?: {
clear?: () => void;
};
};
};
if (typeof globalScope.ResizeObserver === "undefined") {
globalScope.ResizeObserver = ResizeObserverStub;
}
/**
* `matchMedia` no-op polyfill for the JSDOM-backed component tests. JSDOM
* doesn't implement it, but `SidebarProvider` (mounted in `ProtectedLayout`
* so it survives route transitions) and `useIsMobile` both call it on mount
* to read the current breakpoint. Without this stub, every test that
* renders a protected route — including the page-mocked App routing tests —
* crashes before assertions can run.
*
* The stub always reports "does not match" so tests default to the desktop
* layout; tests that need a specific breakpoint should override
* `window.matchMedia` at the test level.
*/
const hasDocument = typeof globalScope.document !== "undefined";
const windowScope = globalScope as unknown as {
window?: { matchMedia?: (query: string) => unknown };
};
if (hasDocument && typeof windowScope.window !== "undefined" && typeof windowScope.window.matchMedia !== "function") {
windowScope.window.matchMedia = (query: string) => ({
matches: false,
media: query,
onchange: null,
addListener: () => {},
removeListener: () => {},
addEventListener: () => {},
removeEventListener: () => {},
dispatchEvent: () => false,
});
}
/**
* In-memory `Storage` polyfill for JSDOM. The shipped implementation in this
* runner is partial (notably missing `clear()`), so any test that touches a
* storage-backed code path needs a working `Storage` swapped in. Tests that
* want a richer mock (e.g. to spy on `setItem`) can still override the
* property at the test level.
*
* Mirrors `src/test-utils/storage.ts`'s `createMemoryStorage()`, but
* duplicated here because `vitest.setup.ts` is type-checked under the
* DOM-less node project — importing the DOM-typed helper would force the
* node tsconfig to include DOM lib.
*/
type AnyStorage = {
length: number;
clear: () => void;
getItem: (key: string) => string | null;
key: (index: number) => string | null;
removeItem: (key: string) => void;
setItem: (key: string, value: string) => void;
};
function createMemoryStorage(): AnyStorage {
const backing = new Map<string, string>();
return {
get length() {
return backing.size;
},
clear: () => {
backing.clear();
},
getItem: (key: string) => backing.get(key) ?? null,
key: (index: number) => Array.from(backing.keys())[index] ?? null,
removeItem: (key: string) => {
backing.delete(key);
},
setItem: (key: string, value: string) => {
backing.set(key, String(value));
},
};
}
// Only patch when running in a JSDOM-like environment. Edge-runtime exposes
// `window.localStorage` / `window.sessionStorage` getters in recent Node
// versions, but reading either without `--localstorage-file=<path>` emits a
// warning. DOM tests are the only ones that need browser storage, so gate this
// on `document` and replace storage without first reading the getter.
if (hasDocument && typeof globalScope.window !== "undefined") {
const win = globalScope.window as unknown as {
localStorage: AnyStorage;
sessionStorage: AnyStorage;
};
Object.defineProperty(win, "localStorage", {
configurable: true,
value: createMemoryStorage(),
});
Object.defineProperty(win, "sessionStorage", {
configurable: true,
value: createMemoryStorage(),
});
beforeEach(() => {
try {
win.localStorage.clear();
} catch {
// Test may have spied on `clear()`; let the test own its own teardown.
}
try {
win.sessionStorage.clear();
} catch {
// Same.
}
});
}