-
Notifications
You must be signed in to change notification settings - Fork 0
/
vitest.setup.ts
93 lines (76 loc) · 2.62 KB
/
vitest.setup.ts
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
import { beforeAll, beforeEach, vi } from 'vitest';
// Cleanup DOM between each test case
// https://stackoverflow.com/a/72984394/12734929
const isJsdomEnviroment = typeof document !== 'undefined';
const sideEffects = isJsdomEnviroment && {
document: {
addEventListener: {
fn: document.addEventListener,
refs: [],
},
keys: Object.keys(document),
},
window: {
addEventListener: {
fn: window.addEventListener,
refs: [],
},
keys: Object.keys(window),
},
};
// Lifecycle Hooks
// -----------------------------------------------------------------------------
beforeAll(async () => {
if (!isJsdomEnviroment) return;
// Spy addEventListener
['document', 'window'].forEach((obj) => {
const { fn } = sideEffects[obj].addEventListener;
const { refs } = sideEffects[obj].addEventListener;
function addEventListenerSpy(type, listener, options) {
// Store listener reference so it can be removed during reset
refs.push({ type, listener, options });
// Call original window.addEventListener
fn(type, listener, options);
}
// Add to default key array to prevent removal during reset
sideEffects[obj].keys.push('addEventListener');
// Replace addEventListener with mock
global[obj].addEventListener = addEventListenerSpy;
});
});
// Reset JSDOM. This attempts to remove side effects from tests, however it does
// not reset all changes made to globals like the window and document
// objects. Tests requiring a full JSDOM reset should be stored in separate
// files, which is only way to do a complete JSDOM reset with Jest.
beforeEach(async () => {
if (!isJsdomEnviroment) return;
const rootElm = document.documentElement;
// Remove attributes on root element
[...rootElm.attributes].forEach((attr) => rootElm.removeAttribute(attr.name));
// Remove elements (faster than setting innerHTML)
while (rootElm.firstChild) {
rootElm.removeChild(rootElm.firstChild);
}
// Remove global listeners and keys
['document', 'window'].forEach((obj) => {
const { refs } = sideEffects[obj].addEventListener;
// Listeners
while (refs.length) {
const { type, listener, options } = refs.pop();
global[obj].removeEventListener(type, listener, options);
}
// Keys
Object.keys(global[obj])
.filter((key) => !sideEffects[obj].keys.includes(key))
.forEach((key) => {
delete global[obj][key];
});
});
// Restore base elements
rootElm.innerHTML = '<head></head><body></body>';
});
// Always use fake times by default
beforeEach(() => {
vi.useFakeTimers();
return () => vi.useRealTimers();
});