|
1 | | -import { GlobalWindow } from 'very-happy-dom' |
| 1 | +import { GlobalWindow } from 'happy-dom' |
2 | 2 |
|
3 | 3 | const win = new GlobalWindow({ url: 'http://localhost' }) |
4 | 4 |
|
5 | | -// Register core browser globals needed by the test environment |
6 | | -const globals: Record<string, unknown> = { |
7 | | - window: win, |
8 | | - document: win.document, |
9 | | - navigator: win.navigator, |
10 | | - location: win.location, |
11 | | - localStorage: win.localStorage, |
12 | | - sessionStorage: win.sessionStorage, |
13 | | - CustomEvent: win.CustomEvent, |
14 | | - HTMLElement: win.HTMLElement, |
15 | | - MutationObserver: win.MutationObserver, |
16 | | - IntersectionObserver: win.IntersectionObserver, |
17 | | - ResizeObserver: win.ResizeObserver, |
18 | | - XMLHttpRequest: win.XMLHttpRequest, |
19 | | - WebSocket: win.WebSocket, |
20 | | - File: win.File, |
21 | | - FileReader: win.FileReader, |
22 | | - FileList: win.FileList, |
23 | | - URL: win.URL, |
24 | | - URLSearchParams: win.URLSearchParams, |
25 | | - Headers: win.Headers, |
26 | | - Request: win.Request, |
27 | | - Response: win.Response, |
28 | | - FormData: win.FormData, |
29 | | - performance: win.performance, |
30 | | - requestAnimationFrame: win.requestAnimationFrame.bind(win), |
31 | | - cancelAnimationFrame: win.cancelAnimationFrame.bind(win), |
32 | | - setTimeout: win.setTimeout.bind(win), |
33 | | - clearTimeout: win.clearTimeout.bind(win), |
34 | | - setInterval: win.setInterval.bind(win), |
35 | | - clearInterval: win.clearInterval.bind(win), |
36 | | - fetch: win.fetch, |
37 | | -} |
38 | | - |
39 | | -for (const [key, value] of Object.entries(globals)) { |
40 | | - if (value !== undefined) { |
41 | | - ;(globalThis as any)[key] = value |
| 5 | +// Register all browser globals from the window instance |
| 6 | +for (const key of Object.getOwnPropertyNames(win)) { |
| 7 | + if (key === 'constructor' || key === 'undefined' || key === 'NaN' || key === 'Infinity') continue |
| 8 | + try { |
| 9 | + ;(globalThis as any)[key] = (win as any)[key] |
| 10 | + } |
| 11 | + catch { |
| 12 | + // Some properties may not be configurable |
42 | 13 | } |
43 | 14 | } |
44 | 15 |
|
45 | | -// Stub Range and Selection APIs needed by rich text editor tests |
46 | | -class MockRange { |
47 | | - startContainer: any = null |
48 | | - startOffset = 0 |
49 | | - endContainer: any = null |
50 | | - endOffset = 0 |
51 | | - collapsed = true |
52 | | - commonAncestorContainer: any = null |
53 | | - |
54 | | - setStart(node: any, offset: number): void { this.startContainer = node; this.startOffset = offset; this.commonAncestorContainer = node } |
55 | | - setEnd(node: any, offset: number): void { this.endContainer = node; this.endOffset = offset } |
56 | | - collapse(toStart?: boolean): void { this.collapsed = true } |
57 | | - cloneRange(): MockRange { return Object.assign(new MockRange(), this) } |
58 | | - selectNode(node: any): void { this.startContainer = node; this.endContainer = node } |
59 | | - selectNodeContents(node: any): void { this.startContainer = node; this.endContainer = node } |
60 | | - deleteContents(): void {} |
61 | | - insertNode(_node: any): void {} |
62 | | - cloneContents(): any { return (win.document as any).createDocumentFragment?.() ?? {} } |
63 | | - toString(): string { return '' } |
64 | | -} |
65 | | - |
66 | | -class MockSelection { |
67 | | - rangeCount = 0 |
68 | | - private ranges: MockRange[] = [] |
69 | | - |
70 | | - getRangeAt(index: number): MockRange { return this.ranges[index] || new MockRange() } |
71 | | - addRange(range: MockRange): void { this.ranges.push(range); this.rangeCount = this.ranges.length } |
72 | | - removeAllRanges(): void { this.ranges = []; this.rangeCount = 0 } |
73 | | - collapse(node: any, offset?: number): void {} |
74 | | - toString(): string { return '' } |
75 | | - get anchorNode(): any { return this.ranges[0]?.startContainer ?? null } |
76 | | - get anchorOffset(): number { return this.ranges[0]?.startOffset ?? 0 } |
77 | | - get focusNode(): any { return this.ranges[0]?.endContainer ?? null } |
78 | | - get focusOffset(): number { return this.ranges[0]?.endOffset ?? 0 } |
79 | | - get isCollapsed(): boolean { return this.ranges[0]?.collapsed ?? true } |
| 16 | +// Register prototype-level getters/methods |
| 17 | +for (const key of Object.getOwnPropertyNames(Object.getPrototypeOf(win))) { |
| 18 | + if (key === 'constructor' || key in globalThis) continue |
| 19 | + try { |
| 20 | + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(win), key) |
| 21 | + if (descriptor?.get) { |
| 22 | + Object.defineProperty(globalThis, key, { |
| 23 | + get: () => (win as any)[key], |
| 24 | + configurable: true, |
| 25 | + }) |
| 26 | + } |
| 27 | + else if (typeof (win as any)[key] === 'function') { |
| 28 | + ;(globalThis as any)[key] = (win as any)[key].bind(win) |
| 29 | + } |
| 30 | + } |
| 31 | + catch { |
| 32 | + // Skip non-configurable properties |
| 33 | + } |
80 | 34 | } |
81 | 35 |
|
82 | | -const mockSelection = new MockSelection() |
83 | | -const doc = (globalThis as any).document |
84 | | -if (doc && !doc.createRange) { |
85 | | - doc.createRange = () => new MockRange() |
86 | | -} |
87 | | -if (!(globalThis as any).window.getSelection) { |
88 | | - ;(globalThis as any).window.getSelection = () => mockSelection |
89 | | -} |
90 | | -if (!(globalThis as any).getSelection) { |
91 | | - ;(globalThis as any).getSelection = () => mockSelection |
92 | | -} |
93 | | -if (doc && !doc.getSelection) { |
94 | | - doc.getSelection = () => mockSelection |
95 | | -} |
| 36 | +// Ensure window is set |
| 37 | +;(globalThis as any).window = win |
0 commit comments