@gjsify/node-globals 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -1
- package/lib/esm/index.js +147 -3
- package/lib/types/index.d.ts +3 -0
- package/package.json +20 -20
- package/src/index.spec.ts +763 -6
- package/src/index.ts +173 -3
- package/src/test.mts +1 -1
- package/tsconfig.json +23 -11
- package/tsconfig.tsbuildinfo +1 -0
- package/lib/cjs/errors.js +0 -2
- package/lib/cjs/index.js +0 -3
- package/lib/esm/errors.js +0 -2
- package/src/errors.ts +0 -3
- package/test.gjs.mjs +0 -34720
- package/test.gjs.mjs.meta.json +0 -1
- package/test.node.mjs +0 -307
- package/tsconfig.types.json +0 -8
package/src/index.spec.ts
CHANGED
|
@@ -1,9 +1,766 @@
|
|
|
1
1
|
import { describe, it, expect } from '@gjsify/unit';
|
|
2
2
|
|
|
3
3
|
export default async () => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
4
|
+
|
|
5
|
+
// --- global / globalThis ---
|
|
6
|
+
await describe('global', async () => {
|
|
7
|
+
await it('should be the same as globalThis', async () => {
|
|
8
|
+
expect((globalThis as any).global).toBe(globalThis);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
await it('globalThis should be defined', async () => {
|
|
12
|
+
expect(globalThis).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// --- process ---
|
|
17
|
+
await describe('process', async () => {
|
|
18
|
+
await it('should be defined on globalThis', async () => {
|
|
19
|
+
expect(typeof (globalThis as any).process).toBe('object');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await it('should have env', async () => {
|
|
23
|
+
expect(typeof (globalThis as any).process.env).toBe('object');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await it('should have cwd function', async () => {
|
|
27
|
+
expect(typeof (globalThis as any).process.cwd).toBe('function');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await it('should have platform property', async () => {
|
|
31
|
+
expect(typeof (globalThis as any).process.platform).toBe('string');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await it('should have argv array', async () => {
|
|
35
|
+
expect(Array.isArray((globalThis as any).process.argv)).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await it('should have pid number', async () => {
|
|
39
|
+
expect(typeof (globalThis as any).process.pid).toBe('number');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// --- Buffer ---
|
|
44
|
+
await describe('Buffer', async () => {
|
|
45
|
+
await it('should be defined on globalThis', async () => {
|
|
46
|
+
expect(typeof (globalThis as any).Buffer).toBe('function');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await it('should create a buffer from string', async () => {
|
|
50
|
+
const buf = (globalThis as any).Buffer.from('hello');
|
|
51
|
+
expect(buf.toString()).toBe('hello');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await it('should have alloc method', async () => {
|
|
55
|
+
expect(typeof (globalThis as any).Buffer.alloc).toBe('function');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await it('should have isBuffer method', async () => {
|
|
59
|
+
expect(typeof (globalThis as any).Buffer.isBuffer).toBe('function');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// --- setImmediate / clearImmediate ---
|
|
64
|
+
await describe('setImmediate', async () => {
|
|
65
|
+
await it('should be a function', async () => {
|
|
66
|
+
expect(typeof setImmediate).toBe('function');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await it('should call the callback asynchronously', async () => {
|
|
70
|
+
const result = await new Promise<string>((resolve) => {
|
|
71
|
+
setImmediate(() => resolve('called'));
|
|
72
|
+
});
|
|
73
|
+
expect(result).toBe('called');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await it('should pass arguments to callback', async () => {
|
|
77
|
+
const result = await new Promise<number>((resolve) => {
|
|
78
|
+
setImmediate((a: number, b: number) => resolve(a + b), 2, 3);
|
|
79
|
+
});
|
|
80
|
+
expect(result).toBe(5);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await describe('clearImmediate', async () => {
|
|
85
|
+
await it('should be a function', async () => {
|
|
86
|
+
expect(typeof clearImmediate).toBe('function');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await it('should cancel a pending setImmediate', async () => {
|
|
90
|
+
let called = false;
|
|
91
|
+
const id = setImmediate(() => { called = true; });
|
|
92
|
+
clearImmediate(id);
|
|
93
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 10));
|
|
94
|
+
expect(called).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// --- setTimeout / clearTimeout ---
|
|
99
|
+
await describe('setTimeout (global)', async () => {
|
|
100
|
+
await it('should be a function', async () => {
|
|
101
|
+
expect(typeof setTimeout).toBe('function');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await it('should call callback after delay', async () => {
|
|
105
|
+
const result = await new Promise<string>((resolve) => {
|
|
106
|
+
setTimeout(() => resolve('timeout'), 10);
|
|
107
|
+
});
|
|
108
|
+
expect(result).toBe('timeout');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await describe('clearTimeout (global)', async () => {
|
|
113
|
+
await it('should be a function', async () => {
|
|
114
|
+
expect(typeof clearTimeout).toBe('function');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// --- setInterval / clearInterval ---
|
|
119
|
+
await describe('setInterval (global)', async () => {
|
|
120
|
+
await it('should be a function', async () => {
|
|
121
|
+
expect(typeof setInterval).toBe('function');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await describe('clearInterval (global)', async () => {
|
|
126
|
+
await it('should be a function', async () => {
|
|
127
|
+
expect(typeof clearInterval).toBe('function');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// --- queueMicrotask ---
|
|
132
|
+
await describe('queueMicrotask', async () => {
|
|
133
|
+
await it('should be a function', async () => {
|
|
134
|
+
expect(typeof queueMicrotask).toBe('function');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await it('should execute callback as a microtask', async () => {
|
|
138
|
+
const result = await new Promise<string>((resolve) => {
|
|
139
|
+
queueMicrotask(() => resolve('microtask'));
|
|
140
|
+
});
|
|
141
|
+
expect(result).toBe('microtask');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// --- structuredClone ---
|
|
146
|
+
// Ported from refs/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests.js
|
|
147
|
+
// Original: 3-Clause BSD license, web-platform-tests contributors.
|
|
148
|
+
// Ported from refs/node-test/parallel/test-structuredClone-global.js
|
|
149
|
+
// Original: MIT license, Node.js contributors.
|
|
150
|
+
await describe('structuredClone', async () => {
|
|
151
|
+
await it('should be a function', async () => {
|
|
152
|
+
expect(typeof structuredClone).toBe('function');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// --- Primitives ---
|
|
156
|
+
await describe('primitives', async () => {
|
|
157
|
+
await it('should clone undefined', async () => {
|
|
158
|
+
expect(structuredClone(undefined)).toBeUndefined();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await it('should clone null', async () => {
|
|
162
|
+
expect(structuredClone(null)).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
await it('should clone true and false', async () => {
|
|
166
|
+
expect(structuredClone(true)).toBe(true);
|
|
167
|
+
expect(structuredClone(false)).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await it('should clone strings', async () => {
|
|
171
|
+
expect(structuredClone('')).toBe('');
|
|
172
|
+
expect(structuredClone('hello')).toBe('hello');
|
|
173
|
+
expect(structuredClone('\u0000')).toBe('\u0000');
|
|
174
|
+
expect(structuredClone('\uD800')).toBe('\uD800');
|
|
175
|
+
expect(structuredClone('\uDC00')).toBe('\uDC00');
|
|
176
|
+
expect(structuredClone('\uDBFF\uDFFD')).toBe('\uDBFF\uDFFD');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
await it('should clone numbers', async () => {
|
|
180
|
+
expect(structuredClone(0)).toBe(0);
|
|
181
|
+
expect(structuredClone(0.2)).toBe(0.2);
|
|
182
|
+
expect(structuredClone(9007199254740992)).toBe(9007199254740992);
|
|
183
|
+
expect(structuredClone(-9007199254740992)).toBe(-9007199254740992);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await it('should clone -0', async () => {
|
|
187
|
+
const cloned = structuredClone(-0);
|
|
188
|
+
expect(Object.is(cloned, -0)).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await it('should clone NaN', async () => {
|
|
192
|
+
const cloned = structuredClone(NaN);
|
|
193
|
+
expect(Number.isNaN(cloned)).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
await it('should clone Infinity and -Infinity', async () => {
|
|
197
|
+
expect(structuredClone(Infinity)).toBe(Infinity);
|
|
198
|
+
expect(structuredClone(-Infinity)).toBe(-Infinity);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await it('should clone BigInt values', async () => {
|
|
202
|
+
expect(structuredClone(0n)).toBe(0n);
|
|
203
|
+
expect(structuredClone(-0n)).toBe(-0n);
|
|
204
|
+
expect(structuredClone(-9007199254740994000n)).toBe(-9007199254740994000n);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// --- Wrapper objects ---
|
|
209
|
+
await describe('wrapper objects', async () => {
|
|
210
|
+
await it('should clone Boolean objects', async () => {
|
|
211
|
+
const original = new Boolean(true);
|
|
212
|
+
const cloned = structuredClone(original);
|
|
213
|
+
expect(cloned instanceof Boolean).toBe(true);
|
|
214
|
+
expect(cloned !== original).toBe(true);
|
|
215
|
+
expect(String(cloned)).toBe('true');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await it('should clone Boolean false object', async () => {
|
|
219
|
+
const original = new Boolean(false);
|
|
220
|
+
const cloned = structuredClone(original);
|
|
221
|
+
expect(cloned instanceof Boolean).toBe(true);
|
|
222
|
+
expect(String(cloned)).toBe('false');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await it('should clone String objects', async () => {
|
|
226
|
+
const original = new String('hello');
|
|
227
|
+
const cloned = structuredClone(original);
|
|
228
|
+
expect(cloned instanceof String).toBe(true);
|
|
229
|
+
expect(cloned !== original).toBe(true);
|
|
230
|
+
expect(String(cloned)).toBe('hello');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
await it('should clone Number objects', async () => {
|
|
234
|
+
const original = new Number(42);
|
|
235
|
+
const cloned = structuredClone(original);
|
|
236
|
+
expect(cloned instanceof Number).toBe(true);
|
|
237
|
+
expect(cloned !== original).toBe(true);
|
|
238
|
+
expect(Number(cloned)).toBe(42);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
await it('should clone Number(-0) object', async () => {
|
|
242
|
+
const original = new Number(-0);
|
|
243
|
+
const cloned = structuredClone(original);
|
|
244
|
+
expect(cloned instanceof Number).toBe(true);
|
|
245
|
+
expect(Object.is(Number(cloned), -0)).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await it('should clone BigInt wrapper object', async () => {
|
|
249
|
+
const original = Object(-9007199254740994n);
|
|
250
|
+
const cloned = structuredClone(original);
|
|
251
|
+
expect(typeof cloned).toBe('object');
|
|
252
|
+
expect(cloned !== original).toBe(true);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// --- Date ---
|
|
257
|
+
await describe('Date', async () => {
|
|
258
|
+
await it('should clone Date objects', async () => {
|
|
259
|
+
const original = new Date(1609459200000);
|
|
260
|
+
const cloned = structuredClone(original);
|
|
261
|
+
expect(cloned instanceof Date).toBe(true);
|
|
262
|
+
expect(cloned !== original).toBe(true);
|
|
263
|
+
expect(cloned.getTime()).toBe(original.getTime());
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await it('should clone Date(0) and Date(-0)', async () => {
|
|
267
|
+
expect(structuredClone(new Date(0)).getTime()).toBe(0);
|
|
268
|
+
expect(structuredClone(new Date(-0)).getTime()).toBe(0);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await it('should clone edge-case dates', async () => {
|
|
272
|
+
expect(structuredClone(new Date(-8.64e15)).getTime()).toBe(-8.64e15);
|
|
273
|
+
expect(structuredClone(new Date(8.64e15)).getTime()).toBe(8.64e15);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// --- RegExp ---
|
|
278
|
+
await describe('RegExp', async () => {
|
|
279
|
+
await it('should clone RegExp preserving flags', async () => {
|
|
280
|
+
const original = /foo/gim;
|
|
281
|
+
const cloned = structuredClone(original);
|
|
282
|
+
expect(cloned instanceof RegExp).toBe(true);
|
|
283
|
+
expect(cloned !== original).toBe(true);
|
|
284
|
+
expect(cloned.source).toBe('foo');
|
|
285
|
+
expect(cloned.global).toBe(true);
|
|
286
|
+
expect(cloned.ignoreCase).toBe(true);
|
|
287
|
+
expect(cloned.multiline).toBe(true);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await it('should reset lastIndex to 0', async () => {
|
|
291
|
+
const original = /foo/g;
|
|
292
|
+
original.lastIndex = 2;
|
|
293
|
+
const cloned = structuredClone(original);
|
|
294
|
+
expect(cloned.lastIndex).toBe(0);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
await it('should clone sticky flag', async () => {
|
|
298
|
+
const original = new RegExp('foo', 'y');
|
|
299
|
+
const cloned = structuredClone(original);
|
|
300
|
+
expect(cloned.sticky).toBe(true);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
await it('should clone unicode flag', async () => {
|
|
304
|
+
const original = new RegExp('foo', 'u');
|
|
305
|
+
const cloned = structuredClone(original);
|
|
306
|
+
expect(cloned.unicode).toBe(true);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
await it('should clone empty RegExp', async () => {
|
|
310
|
+
const cloned = structuredClone(new RegExp(''));
|
|
311
|
+
expect(cloned instanceof RegExp).toBe(true);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// --- Error types ---
|
|
316
|
+
await describe('Error', async () => {
|
|
317
|
+
await it('should clone empty Error', async () => {
|
|
318
|
+
const cloned = structuredClone(new Error());
|
|
319
|
+
expect(cloned instanceof Error).toBe(true);
|
|
320
|
+
expect(cloned.message).toBe('');
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
await it('should clone Error with message', async () => {
|
|
324
|
+
const original = new Error('test message');
|
|
325
|
+
const cloned = structuredClone(original);
|
|
326
|
+
expect(cloned instanceof Error).toBe(true);
|
|
327
|
+
expect(cloned.message).toBe('test message');
|
|
328
|
+
expect(cloned !== original).toBe(true);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
await it('should clone Error with cause', async () => {
|
|
332
|
+
const original = new Error('msg', { cause: 'my cause' });
|
|
333
|
+
const cloned = structuredClone(original);
|
|
334
|
+
expect(cloned.message).toBe('msg');
|
|
335
|
+
expect((cloned as any).cause).toBe('my cause');
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
await it('should clone all error subtypes', async () => {
|
|
339
|
+
const constructors = [EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError];
|
|
340
|
+
for (const Ctor of constructors) {
|
|
341
|
+
const original = new Ctor('test');
|
|
342
|
+
const cloned = structuredClone(original);
|
|
343
|
+
expect(cloned instanceof Ctor).toBe(true);
|
|
344
|
+
expect(cloned.message).toBe('test');
|
|
345
|
+
expect(cloned.name).toBe(Ctor.name);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
await it('should not preserve custom properties on Error', async () => {
|
|
350
|
+
const original = new Error('test') as any;
|
|
351
|
+
original.foo = 'bar';
|
|
352
|
+
const cloned = structuredClone(original) as any;
|
|
353
|
+
expect(cloned.foo).toBeUndefined();
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// --- ArrayBuffer ---
|
|
358
|
+
await describe('ArrayBuffer', async () => {
|
|
359
|
+
await it('should clone ArrayBuffer', async () => {
|
|
360
|
+
const original = new ArrayBuffer(16);
|
|
361
|
+
const view = new Uint8Array(original);
|
|
362
|
+
view[0] = 42;
|
|
363
|
+
view[15] = 99;
|
|
364
|
+
const cloned = structuredClone(original);
|
|
365
|
+
expect(cloned instanceof ArrayBuffer).toBe(true);
|
|
366
|
+
expect(cloned !== original).toBe(true);
|
|
367
|
+
expect(cloned.byteLength).toBe(16);
|
|
368
|
+
const clonedView = new Uint8Array(cloned);
|
|
369
|
+
expect(clonedView[0]).toBe(42);
|
|
370
|
+
expect(clonedView[15]).toBe(99);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
await it('cloned ArrayBuffer should be independent', async () => {
|
|
374
|
+
const original = new ArrayBuffer(4);
|
|
375
|
+
new Uint8Array(original)[0] = 1;
|
|
376
|
+
const cloned = structuredClone(original);
|
|
377
|
+
new Uint8Array(original)[0] = 99;
|
|
378
|
+
expect(new Uint8Array(cloned)[0]).toBe(1);
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// --- TypedArrays ---
|
|
383
|
+
await describe('TypedArrays', async () => {
|
|
384
|
+
await it('should clone Uint8Array', async () => {
|
|
385
|
+
const original = new Uint8Array([1, 2, 3, 4]);
|
|
386
|
+
const cloned = structuredClone(original);
|
|
387
|
+
expect(cloned instanceof Uint8Array).toBe(true);
|
|
388
|
+
expect(cloned !== original).toBe(true);
|
|
389
|
+
expect(cloned.length).toBe(4);
|
|
390
|
+
expect(cloned[0]).toBe(1);
|
|
391
|
+
expect(cloned[3]).toBe(4);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
await it('should clone Int16Array', async () => {
|
|
395
|
+
const original = new Int16Array([-1, 0, 32767]);
|
|
396
|
+
const cloned = structuredClone(original);
|
|
397
|
+
expect(cloned instanceof Int16Array).toBe(true);
|
|
398
|
+
expect(cloned[0]).toBe(-1);
|
|
399
|
+
expect(cloned[2]).toBe(32767);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
await it('should clone Float64Array', async () => {
|
|
403
|
+
const original = new Float64Array([1.5, -0, NaN, Infinity]);
|
|
404
|
+
const cloned = structuredClone(original);
|
|
405
|
+
expect(cloned instanceof Float64Array).toBe(true);
|
|
406
|
+
expect(cloned[0]).toBe(1.5);
|
|
407
|
+
expect(Object.is(cloned[1], -0)).toBe(true);
|
|
408
|
+
expect(Number.isNaN(cloned[2])).toBe(true);
|
|
409
|
+
expect(cloned[3]).toBe(Infinity);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
await it('should clone BigInt64Array', async () => {
|
|
413
|
+
const original = new BigInt64Array([0n, -1n, 9007199254740993n]);
|
|
414
|
+
const cloned = structuredClone(original);
|
|
415
|
+
expect(cloned instanceof BigInt64Array).toBe(true);
|
|
416
|
+
expect(cloned[0]).toBe(0n);
|
|
417
|
+
expect(cloned[1]).toBe(-1n);
|
|
418
|
+
expect(cloned[2]).toBe(9007199254740993n);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
await it('cloned TypedArray should be independent', async () => {
|
|
422
|
+
const original = new Uint8Array([10, 20]);
|
|
423
|
+
const cloned = structuredClone(original);
|
|
424
|
+
original[0] = 99;
|
|
425
|
+
expect(cloned[0]).toBe(10);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// --- DataView ---
|
|
430
|
+
await describe('DataView', async () => {
|
|
431
|
+
await it('should clone DataView', async () => {
|
|
432
|
+
const buf = new ArrayBuffer(8);
|
|
433
|
+
const original = new DataView(buf, 2, 4);
|
|
434
|
+
original.setUint8(0, 42);
|
|
435
|
+
const cloned = structuredClone(original);
|
|
436
|
+
expect(cloned instanceof DataView).toBe(true);
|
|
437
|
+
expect(cloned !== original).toBe(true);
|
|
438
|
+
expect(cloned.byteOffset).toBe(2);
|
|
439
|
+
expect(cloned.byteLength).toBe(4);
|
|
440
|
+
expect(cloned.getUint8(0)).toBe(42);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// --- Map ---
|
|
445
|
+
await describe('Map', async () => {
|
|
446
|
+
await it('should clone Map with entries', async () => {
|
|
447
|
+
const original = new Map([['a', 1], ['b', 2]]);
|
|
448
|
+
const cloned = structuredClone(original);
|
|
449
|
+
expect(cloned instanceof Map).toBe(true);
|
|
450
|
+
expect(cloned !== original).toBe(true);
|
|
451
|
+
expect(cloned.size).toBe(2);
|
|
452
|
+
expect(cloned.get('a')).toBe(1);
|
|
453
|
+
expect(cloned.get('b')).toBe(2);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
await it('should deep clone Map values', async () => {
|
|
457
|
+
const inner = { x: 1 };
|
|
458
|
+
const original = new Map([['key', inner]]);
|
|
459
|
+
const cloned = structuredClone(original);
|
|
460
|
+
expect(cloned.get('key') !== inner).toBe(true);
|
|
461
|
+
expect((cloned.get('key') as any).x).toBe(1);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// --- Set ---
|
|
466
|
+
await describe('Set', async () => {
|
|
467
|
+
await it('should clone Set with values', async () => {
|
|
468
|
+
const original = new Set([1, 'two', 3]);
|
|
469
|
+
const cloned = structuredClone(original);
|
|
470
|
+
expect(cloned instanceof Set).toBe(true);
|
|
471
|
+
expect(cloned !== original).toBe(true);
|
|
472
|
+
expect(cloned.size).toBe(3);
|
|
473
|
+
expect(cloned.has(1)).toBe(true);
|
|
474
|
+
expect(cloned.has('two')).toBe(true);
|
|
475
|
+
expect(cloned.has(3)).toBe(true);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
await it('should deep clone Set values', async () => {
|
|
479
|
+
const inner = { x: 1 };
|
|
480
|
+
const original = new Set([inner]);
|
|
481
|
+
const cloned = structuredClone(original);
|
|
482
|
+
const clonedInner = [...cloned][0] as any;
|
|
483
|
+
expect(clonedInner !== inner).toBe(true);
|
|
484
|
+
expect(clonedInner.x).toBe(1);
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// --- Blob and File ---
|
|
489
|
+
await describe('Blob and File', async () => {
|
|
490
|
+
await it('should clone Blob preserving type and size', async () => {
|
|
491
|
+
if (typeof Blob === 'undefined') return; // skip if Blob not available
|
|
492
|
+
const original = new Blob(['hello'], { type: 'text/plain' });
|
|
493
|
+
const cloned = structuredClone(original);
|
|
494
|
+
expect(cloned instanceof Blob).toBe(true);
|
|
495
|
+
expect(cloned !== original).toBe(true);
|
|
496
|
+
expect(cloned.size).toBe(original.size);
|
|
497
|
+
expect(cloned.type).toBe('text/plain');
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
await it('should clone File preserving name and lastModified', async () => {
|
|
501
|
+
if (typeof File === 'undefined') return; // skip if File not available
|
|
502
|
+
const original = new File(['data'], 'test.txt', { type: 'text/plain', lastModified: 1609459200000 });
|
|
503
|
+
const cloned = structuredClone(original);
|
|
504
|
+
expect(cloned instanceof File).toBe(true);
|
|
505
|
+
expect(cloned !== original).toBe(true);
|
|
506
|
+
expect(cloned.name).toBe('test.txt');
|
|
507
|
+
expect(cloned.lastModified).toBe(1609459200000);
|
|
508
|
+
expect(cloned.type).toBe('text/plain');
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// --- Circular and shared references ---
|
|
513
|
+
await describe('circular and shared references', async () => {
|
|
514
|
+
await it('should handle self-referencing object', async () => {
|
|
515
|
+
const original: any = { name: 'self' };
|
|
516
|
+
original.self = original;
|
|
517
|
+
const cloned = structuredClone(original);
|
|
518
|
+
expect(cloned !== original).toBe(true);
|
|
519
|
+
expect(cloned.name).toBe('self');
|
|
520
|
+
expect(cloned.self).toBe(cloned);
|
|
521
|
+
expect(cloned.self !== original).toBe(true);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
await it('should handle self-referencing array', async () => {
|
|
525
|
+
const original: any[] = [1, 2];
|
|
526
|
+
original.push(original);
|
|
527
|
+
const cloned = structuredClone(original);
|
|
528
|
+
expect(cloned !== original).toBe(true);
|
|
529
|
+
expect(cloned[0]).toBe(1);
|
|
530
|
+
expect(cloned[1]).toBe(2);
|
|
531
|
+
expect(cloned[2]).toBe(cloned);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
await it('should preserve shared references (identity)', async () => {
|
|
535
|
+
const shared = { value: 42 };
|
|
536
|
+
const original = { a: shared, b: shared };
|
|
537
|
+
const cloned = structuredClone(original);
|
|
538
|
+
expect(cloned.a !== shared).toBe(true);
|
|
539
|
+
expect(cloned.a).toBe(cloned.b);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
await it('should handle circular reference in Map', async () => {
|
|
543
|
+
const original = new Map<string, any>();
|
|
544
|
+
original.set('self', original);
|
|
545
|
+
const cloned = structuredClone(original);
|
|
546
|
+
expect(cloned !== original).toBe(true);
|
|
547
|
+
expect(cloned.get('self')).toBe(cloned);
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// --- Prototype and property behavior ---
|
|
552
|
+
await describe('prototype and property behavior', async () => {
|
|
553
|
+
await it('should not clone prototype properties', async () => {
|
|
554
|
+
class Foo { bar() { return 42; } }
|
|
555
|
+
const original = new Foo();
|
|
556
|
+
const cloned = structuredClone(original) as any;
|
|
557
|
+
expect(cloned.bar).toBeUndefined();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
await it('should not clone non-enumerable properties', async () => {
|
|
561
|
+
const original: any = {};
|
|
562
|
+
Object.defineProperty(original, 'hidden', { value: 42, enumerable: false });
|
|
563
|
+
original.visible = 1;
|
|
564
|
+
const cloned = structuredClone(original);
|
|
565
|
+
expect((cloned as any).visible).toBe(1);
|
|
566
|
+
expect((cloned as any).hidden).toBeUndefined();
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
await it('should deep clone nested objects in arrays', async () => {
|
|
570
|
+
const original = [{ a: 1 }, { b: 2 }];
|
|
571
|
+
const cloned = structuredClone(original);
|
|
572
|
+
expect(cloned[0] !== original[0]).toBe(true);
|
|
573
|
+
expect((cloned[0] as any).a).toBe(1);
|
|
574
|
+
expect((cloned[1] as any).b).toBe(2);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
await it('should clone sparse arrays', async () => {
|
|
578
|
+
const original = [1, , 3] as any[]; // eslint-disable-line no-sparse-arrays
|
|
579
|
+
const cloned = structuredClone(original);
|
|
580
|
+
expect(cloned.length).toBe(3);
|
|
581
|
+
expect(cloned[0]).toBe(1);
|
|
582
|
+
expect(1 in cloned).toBe(false);
|
|
583
|
+
expect(cloned[2]).toBe(3);
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// --- Non-cloneable types (DataCloneError) ---
|
|
588
|
+
await describe('non-cloneable types', async () => {
|
|
589
|
+
await it('should throw for functions', async () => {
|
|
590
|
+
let threw = false;
|
|
591
|
+
try {
|
|
592
|
+
structuredClone((() => {}) as any);
|
|
593
|
+
} catch (e: any) {
|
|
594
|
+
threw = true;
|
|
595
|
+
expect(e.name).toBe('DataCloneError');
|
|
596
|
+
}
|
|
597
|
+
expect(threw).toBe(true);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
await it('should throw for symbols', async () => {
|
|
601
|
+
let threw = false;
|
|
602
|
+
try {
|
|
603
|
+
structuredClone(Symbol('test') as any);
|
|
604
|
+
} catch (e: any) {
|
|
605
|
+
threw = true;
|
|
606
|
+
expect(e.name).toBe('DataCloneError');
|
|
607
|
+
}
|
|
608
|
+
expect(threw).toBe(true);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
await it('should throw for WeakMap', async () => {
|
|
612
|
+
let threw = false;
|
|
613
|
+
try {
|
|
614
|
+
structuredClone(new WeakMap() as any);
|
|
615
|
+
} catch (e: any) {
|
|
616
|
+
threw = true;
|
|
617
|
+
expect(e.name).toBe('DataCloneError');
|
|
618
|
+
}
|
|
619
|
+
expect(threw).toBe(true);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
await it('should throw for WeakSet', async () => {
|
|
623
|
+
let threw = false;
|
|
624
|
+
try {
|
|
625
|
+
structuredClone(new WeakSet() as any);
|
|
626
|
+
} catch (e: any) {
|
|
627
|
+
threw = true;
|
|
628
|
+
expect(e.name).toBe('DataCloneError');
|
|
629
|
+
}
|
|
630
|
+
expect(threw).toBe(true);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
await it('should throw for WeakRef', async () => {
|
|
634
|
+
let threw = false;
|
|
635
|
+
try {
|
|
636
|
+
structuredClone(new WeakRef({}) as any);
|
|
637
|
+
} catch (e: any) {
|
|
638
|
+
threw = true;
|
|
639
|
+
expect(e.name).toBe('DataCloneError');
|
|
640
|
+
}
|
|
641
|
+
expect(threw).toBe(true);
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// --- Complex nested structures ---
|
|
646
|
+
await describe('nested structures', async () => {
|
|
647
|
+
await it('should clone Maps containing Sets', async () => {
|
|
648
|
+
const original = new Map([['items', new Set([1, 2, 3])]]);
|
|
649
|
+
const cloned = structuredClone(original);
|
|
650
|
+
const clonedSet = cloned.get('items') as Set<number>;
|
|
651
|
+
expect(clonedSet instanceof Set).toBe(true);
|
|
652
|
+
expect(clonedSet.size).toBe(3);
|
|
653
|
+
expect(clonedSet.has(1)).toBe(true);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
await it('should clone objects with mixed types', async () => {
|
|
657
|
+
const original = {
|
|
658
|
+
date: new Date(0),
|
|
659
|
+
regexp: /test/gi,
|
|
660
|
+
map: new Map([['key', 'value']]),
|
|
661
|
+
set: new Set([1, 2]),
|
|
662
|
+
nested: { arr: [1, new Uint8Array([10, 20])] },
|
|
663
|
+
};
|
|
664
|
+
const cloned = structuredClone(original);
|
|
665
|
+
expect(cloned !== original).toBe(true);
|
|
666
|
+
expect(cloned.date instanceof Date).toBe(true);
|
|
667
|
+
expect(cloned.date.getTime()).toBe(0);
|
|
668
|
+
expect(cloned.regexp instanceof RegExp).toBe(true);
|
|
669
|
+
expect(cloned.regexp.source).toBe('test');
|
|
670
|
+
expect(cloned.map instanceof Map).toBe(true);
|
|
671
|
+
expect(cloned.map.get('key')).toBe('value');
|
|
672
|
+
expect(cloned.set instanceof Set).toBe(true);
|
|
673
|
+
expect(cloned.set.has(1)).toBe(true);
|
|
674
|
+
expect((cloned.nested.arr[1] as Uint8Array)[0]).toBe(10);
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// --- TextEncoder / TextDecoder ---
|
|
680
|
+
await describe('TextEncoder', async () => {
|
|
681
|
+
await it('should be a function', async () => {
|
|
682
|
+
expect(typeof TextEncoder).toBe('function');
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
await it('should encode a string to Uint8Array', async () => {
|
|
686
|
+
const encoder = new TextEncoder();
|
|
687
|
+
const encoded = encoder.encode('hello');
|
|
688
|
+
expect(encoded instanceof Uint8Array).toBe(true);
|
|
689
|
+
expect(encoded.length).toBe(5);
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
await describe('TextDecoder', async () => {
|
|
694
|
+
await it('should be a function', async () => {
|
|
695
|
+
expect(typeof TextDecoder).toBe('function');
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
await it('should decode Uint8Array to string', async () => {
|
|
699
|
+
const decoder = new TextDecoder();
|
|
700
|
+
const decoded = decoder.decode(new Uint8Array([104, 101, 108, 108, 111]));
|
|
701
|
+
expect(decoded).toBe('hello');
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
// --- atob / btoa ---
|
|
706
|
+
await describe('atob / btoa', async () => {
|
|
707
|
+
await it('btoa should be a function', async () => {
|
|
708
|
+
expect(typeof btoa).toBe('function');
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
await it('atob should be a function', async () => {
|
|
712
|
+
expect(typeof atob).toBe('function');
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
await it('should round-trip encode/decode', async () => {
|
|
716
|
+
const encoded = btoa('Hello, World!');
|
|
717
|
+
expect(typeof encoded).toBe('string');
|
|
718
|
+
expect(atob(encoded)).toBe('Hello, World!');
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
// --- URL / URLSearchParams ---
|
|
723
|
+
await describe('URL (global)', async () => {
|
|
724
|
+
await it('should be a function', async () => {
|
|
725
|
+
expect(typeof URL).toBe('function');
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
await it('should parse a URL', async () => {
|
|
729
|
+
const url = new URL('https://example.com/path?key=val');
|
|
730
|
+
expect(url.hostname).toBe('example.com');
|
|
731
|
+
expect(url.pathname).toBe('/path');
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
await describe('URLSearchParams (global)', async () => {
|
|
736
|
+
await it('should be a function', async () => {
|
|
737
|
+
expect(typeof URLSearchParams).toBe('function');
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
// --- Error.captureStackTrace ---
|
|
742
|
+
await describe('Error.captureStackTrace', async () => {
|
|
743
|
+
await it('should be a function', async () => {
|
|
744
|
+
expect(typeof (Error as any).captureStackTrace).toBe('function');
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
await it('should add a stack property to the target object', async () => {
|
|
748
|
+
const obj: any = {};
|
|
749
|
+
(Error as any).captureStackTrace(obj);
|
|
750
|
+
expect(typeof obj.stack).toBe('string');
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// --- console ---
|
|
755
|
+
await describe('console (global)', async () => {
|
|
756
|
+
await it('should be an object', async () => {
|
|
757
|
+
expect(typeof console).toBe('object');
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
await it('should have log/warn/error methods', async () => {
|
|
761
|
+
expect(typeof console.log).toBe('function');
|
|
762
|
+
expect(typeof console.warn).toBe('function');
|
|
763
|
+
expect(typeof console.error).toBe('function');
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
};
|