@ricsam/isolate-test-environment 0.0.1 → 0.1.2

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.
@@ -0,0 +1,643 @@
1
+ // @bun @bun-cjs
2
+ (function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
7
+ var __toCommonJS = (from) => {
8
+ var entry = __moduleCache.get(from), desc;
9
+ if (entry)
10
+ return entry;
11
+ entry = __defProp({}, "__esModule", { value: true });
12
+ if (from && typeof from === "object" || typeof from === "function")
13
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
14
+ get: () => from[key],
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ }));
17
+ __moduleCache.set(from, entry);
18
+ return entry;
19
+ };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
29
+
30
+ // packages/test-environment/src/index.ts
31
+ var exports_src = {};
32
+ __export(exports_src, {
33
+ setupTestEnvironment: () => setupTestEnvironment,
34
+ runTests: () => runTests
35
+ });
36
+ module.exports = __toCommonJS(exports_src);
37
+ var testEnvironmentCode = `
38
+ (function() {
39
+ // ============================================================
40
+ // Internal State
41
+ // ============================================================
42
+
43
+ function createSuite(name, skip = false, only = false) {
44
+ return {
45
+ name,
46
+ tests: [],
47
+ children: [],
48
+ beforeAll: [],
49
+ afterAll: [],
50
+ beforeEach: [],
51
+ afterEach: [],
52
+ skip,
53
+ only,
54
+ };
55
+ }
56
+
57
+ const rootSuite = createSuite('root');
58
+ let currentSuite = rootSuite;
59
+ const suiteStack = [rootSuite];
60
+
61
+ // ============================================================
62
+ // Deep Equality Helper
63
+ // ============================================================
64
+
65
+ function deepEqual(a, b) {
66
+ if (a === b) return true;
67
+ if (typeof a !== typeof b) return false;
68
+ if (typeof a !== 'object' || a === null || b === null) return false;
69
+
70
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
71
+
72
+ const keysA = Object.keys(a);
73
+ const keysB = Object.keys(b);
74
+ if (keysA.length !== keysB.length) return false;
75
+
76
+ for (const key of keysA) {
77
+ if (!keysB.includes(key)) return false;
78
+ if (!deepEqual(a[key], b[key])) return false;
79
+ }
80
+ return true;
81
+ }
82
+
83
+ function strictDeepEqual(a, b) {
84
+ if (a === b) return true;
85
+ if (typeof a !== typeof b) return false;
86
+ if (typeof a !== 'object' || a === null || b === null) return false;
87
+
88
+ // Check prototypes
89
+ if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;
90
+
91
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
92
+
93
+ // For arrays, check sparse arrays (holes)
94
+ if (Array.isArray(a)) {
95
+ if (a.length !== b.length) return false;
96
+ for (let i = 0; i < a.length; i++) {
97
+ const aHasIndex = i in a;
98
+ const bHasIndex = i in b;
99
+ if (aHasIndex !== bHasIndex) return false;
100
+ if (aHasIndex && !strictDeepEqual(a[i], b[i])) return false;
101
+ }
102
+ return true;
103
+ }
104
+
105
+ // Check for undefined properties vs missing properties
106
+ const keysA = Object.keys(a);
107
+ const keysB = Object.keys(b);
108
+ if (keysA.length !== keysB.length) return false;
109
+
110
+ for (const key of keysA) {
111
+ if (!keysB.includes(key)) return false;
112
+ if (!strictDeepEqual(a[key], b[key])) return false;
113
+ }
114
+
115
+ // Check for symbol properties
116
+ const symbolsA = Object.getOwnPropertySymbols(a);
117
+ const symbolsB = Object.getOwnPropertySymbols(b);
118
+ if (symbolsA.length !== symbolsB.length) return false;
119
+
120
+ for (const sym of symbolsA) {
121
+ if (!symbolsB.includes(sym)) return false;
122
+ if (!strictDeepEqual(a[sym], b[sym])) return false;
123
+ }
124
+
125
+ return true;
126
+ }
127
+
128
+ function getNestedProperty(obj, path) {
129
+ const parts = path.split('.');
130
+ let current = obj;
131
+ for (const part of parts) {
132
+ if (current == null || !(part in current)) {
133
+ return { exists: false };
134
+ }
135
+ current = current[part];
136
+ }
137
+ return { exists: true, value: current };
138
+ }
139
+
140
+ function formatValue(val) {
141
+ if (val === null) return 'null';
142
+ if (val === undefined) return 'undefined';
143
+ if (typeof val === 'string') return JSON.stringify(val);
144
+ if (typeof val === 'object') {
145
+ try {
146
+ return JSON.stringify(val);
147
+ } catch {
148
+ return String(val);
149
+ }
150
+ }
151
+ return String(val);
152
+ }
153
+
154
+ // ============================================================
155
+ // expect() Implementation
156
+ // ============================================================
157
+
158
+ function expect(actual) {
159
+ function createMatchers(negated = false) {
160
+ const assert = (condition, message) => {
161
+ const pass = negated ? !condition : condition;
162
+ if (!pass) {
163
+ throw new Error(message);
164
+ }
165
+ };
166
+
167
+ const matchers = {
168
+ toBe(expected) {
169
+ assert(
170
+ actual === expected,
171
+ negated
172
+ ? \`Expected \${formatValue(actual)} not to be \${formatValue(expected)}\`
173
+ : \`Expected \${formatValue(actual)} to be \${formatValue(expected)}\`
174
+ );
175
+ },
176
+
177
+ toEqual(expected) {
178
+ assert(
179
+ deepEqual(actual, expected),
180
+ negated
181
+ ? \`Expected \${formatValue(actual)} not to equal \${formatValue(expected)}\`
182
+ : \`Expected \${formatValue(actual)} to equal \${formatValue(expected)}\`
183
+ );
184
+ },
185
+
186
+ toStrictEqual(expected) {
187
+ assert(
188
+ strictDeepEqual(actual, expected),
189
+ negated
190
+ ? \`Expected \${formatValue(actual)} not to strictly equal \${formatValue(expected)}\`
191
+ : \`Expected \${formatValue(actual)} to strictly equal \${formatValue(expected)}\`
192
+ );
193
+ },
194
+
195
+ toBeTruthy() {
196
+ assert(
197
+ !!actual,
198
+ negated
199
+ ? \`Expected \${formatValue(actual)} not to be truthy\`
200
+ : \`Expected \${formatValue(actual)} to be truthy\`
201
+ );
202
+ },
203
+
204
+ toBeFalsy() {
205
+ assert(
206
+ !actual,
207
+ negated
208
+ ? \`Expected \${formatValue(actual)} not to be falsy\`
209
+ : \`Expected \${formatValue(actual)} to be falsy\`
210
+ );
211
+ },
212
+
213
+ toBeNull() {
214
+ assert(
215
+ actual === null,
216
+ negated
217
+ ? \`Expected \${formatValue(actual)} not to be null\`
218
+ : \`Expected \${formatValue(actual)} to be null\`
219
+ );
220
+ },
221
+
222
+ toBeUndefined() {
223
+ assert(
224
+ actual === undefined,
225
+ negated
226
+ ? \`Expected \${formatValue(actual)} not to be undefined\`
227
+ : \`Expected \${formatValue(actual)} to be undefined\`
228
+ );
229
+ },
230
+
231
+ toBeDefined() {
232
+ assert(
233
+ actual !== undefined,
234
+ negated
235
+ ? \`Expected \${formatValue(actual)} not to be defined\`
236
+ : \`Expected \${formatValue(actual)} to be defined\`
237
+ );
238
+ },
239
+
240
+ toContain(item) {
241
+ let contains = false;
242
+ if (Array.isArray(actual)) {
243
+ contains = actual.includes(item);
244
+ } else if (typeof actual === 'string') {
245
+ contains = actual.includes(item);
246
+ }
247
+ assert(
248
+ contains,
249
+ negated
250
+ ? \`Expected \${formatValue(actual)} not to contain \${formatValue(item)}\`
251
+ : \`Expected \${formatValue(actual)} to contain \${formatValue(item)}\`
252
+ );
253
+ },
254
+
255
+ toThrow(expected) {
256
+ if (typeof actual !== 'function') {
257
+ throw new Error('toThrow requires a function');
258
+ }
259
+
260
+ let threw = false;
261
+ let error = null;
262
+ try {
263
+ actual();
264
+ } catch (e) {
265
+ threw = true;
266
+ error = e;
267
+ }
268
+
269
+ if (expected !== undefined) {
270
+ const matches = threw && (
271
+ (typeof expected === 'string' && error.message.includes(expected)) ||
272
+ (expected instanceof RegExp && expected.test(error.message)) ||
273
+ (typeof expected === 'function' && error instanceof expected)
274
+ );
275
+ assert(
276
+ matches,
277
+ negated
278
+ ? \`Expected function not to throw \${formatValue(expected)}\`
279
+ : \`Expected function to throw \${formatValue(expected)}, but \${threw ? \`threw: \${error.message}\` : 'did not throw'}\`
280
+ );
281
+ } else {
282
+ assert(
283
+ threw,
284
+ negated
285
+ ? \`Expected function not to throw\`
286
+ : \`Expected function to throw\`
287
+ );
288
+ }
289
+ },
290
+
291
+ toBeInstanceOf(cls) {
292
+ assert(
293
+ actual instanceof cls,
294
+ negated
295
+ ? \`Expected \${formatValue(actual)} not to be instance of \${cls.name || cls}\`
296
+ : \`Expected \${formatValue(actual)} to be instance of \${cls.name || cls}\`
297
+ );
298
+ },
299
+
300
+ toHaveLength(length) {
301
+ const actualLength = actual?.length;
302
+ assert(
303
+ actualLength === length,
304
+ negated
305
+ ? \`Expected length not to be \${length}, but got \${actualLength}\`
306
+ : \`Expected length to be \${length}, but got \${actualLength}\`
307
+ );
308
+ },
309
+
310
+ toMatch(pattern) {
311
+ let matches = false;
312
+ if (typeof pattern === 'string') {
313
+ matches = actual.includes(pattern);
314
+ } else if (pattern instanceof RegExp) {
315
+ matches = pattern.test(actual);
316
+ }
317
+ assert(
318
+ matches,
319
+ negated
320
+ ? \`Expected \${formatValue(actual)} not to match \${pattern}\`
321
+ : \`Expected \${formatValue(actual)} to match \${pattern}\`
322
+ );
323
+ },
324
+
325
+ toHaveProperty(path, value) {
326
+ const prop = getNestedProperty(actual, path);
327
+ const hasProperty = prop.exists;
328
+ const valueMatches = arguments.length < 2 || deepEqual(prop.value, value);
329
+
330
+ assert(
331
+ hasProperty && valueMatches,
332
+ negated
333
+ ? \`Expected \${formatValue(actual)} not to have property \${path}\${arguments.length >= 2 ? \` with value \${formatValue(value)}\` : ''}\`
334
+ : \`Expected \${formatValue(actual)} to have property \${path}\${arguments.length >= 2 ? \` with value \${formatValue(value)}\` : ''}\`
335
+ );
336
+ },
337
+ };
338
+
339
+ return matchers;
340
+ }
341
+
342
+ const matchers = createMatchers(false);
343
+ matchers.not = createMatchers(true);
344
+
345
+ return matchers;
346
+ }
347
+
348
+ // ============================================================
349
+ // Test Registration Functions
350
+ // ============================================================
351
+
352
+ function describe(name, fn) {
353
+ const suite = createSuite(name);
354
+ currentSuite.children.push(suite);
355
+
356
+ const parentSuite = currentSuite;
357
+ currentSuite = suite;
358
+ suiteStack.push(suite);
359
+
360
+ fn();
361
+
362
+ suiteStack.pop();
363
+ currentSuite = parentSuite;
364
+ }
365
+
366
+ describe.skip = function(name, fn) {
367
+ const suite = createSuite(name, true, false);
368
+ currentSuite.children.push(suite);
369
+
370
+ const parentSuite = currentSuite;
371
+ currentSuite = suite;
372
+ suiteStack.push(suite);
373
+
374
+ fn();
375
+
376
+ suiteStack.pop();
377
+ currentSuite = parentSuite;
378
+ };
379
+
380
+ describe.only = function(name, fn) {
381
+ const suite = createSuite(name, false, true);
382
+ currentSuite.children.push(suite);
383
+
384
+ const parentSuite = currentSuite;
385
+ currentSuite = suite;
386
+ suiteStack.push(suite);
387
+
388
+ fn();
389
+
390
+ suiteStack.pop();
391
+ currentSuite = parentSuite;
392
+ };
393
+
394
+ function test(name, fn) {
395
+ currentSuite.tests.push({
396
+ name,
397
+ fn,
398
+ skip: false,
399
+ only: false,
400
+ });
401
+ }
402
+
403
+ test.skip = function(name, fn) {
404
+ currentSuite.tests.push({
405
+ name,
406
+ fn,
407
+ skip: true,
408
+ only: false,
409
+ });
410
+ };
411
+
412
+ test.only = function(name, fn) {
413
+ currentSuite.tests.push({
414
+ name,
415
+ fn,
416
+ skip: false,
417
+ only: true,
418
+ });
419
+ };
420
+
421
+ test.todo = function(name) {
422
+ currentSuite.tests.push({
423
+ name,
424
+ fn: null,
425
+ skip: false,
426
+ only: false,
427
+ todo: true,
428
+ });
429
+ };
430
+
431
+ const it = test;
432
+ it.skip = test.skip;
433
+ it.only = test.only;
434
+ it.todo = test.todo;
435
+
436
+ // ============================================================
437
+ // Lifecycle Hooks
438
+ // ============================================================
439
+
440
+ function beforeEach(fn) {
441
+ currentSuite.beforeEach.push(fn);
442
+ }
443
+
444
+ function afterEach(fn) {
445
+ currentSuite.afterEach.push(fn);
446
+ }
447
+
448
+ function beforeAll(fn) {
449
+ currentSuite.beforeAll.push(fn);
450
+ }
451
+
452
+ function afterAll(fn) {
453
+ currentSuite.afterAll.push(fn);
454
+ }
455
+
456
+ // ============================================================
457
+ // Test Runner
458
+ // ============================================================
459
+
460
+ function checkForOnly(suite) {
461
+ if (suite.only) return true;
462
+ for (const t of suite.tests) {
463
+ if (t.only) return true;
464
+ }
465
+ for (const child of suite.children) {
466
+ if (checkForOnly(child)) return true;
467
+ }
468
+ return false;
469
+ }
470
+
471
+ function suiteHasOnly(suite) {
472
+ if (suite.only) return true;
473
+ for (const t of suite.tests) {
474
+ if (t.only) return true;
475
+ }
476
+ for (const child of suite.children) {
477
+ if (suiteHasOnly(child)) return true;
478
+ }
479
+ return false;
480
+ }
481
+
482
+ async function __runAllTests() {
483
+ const results = [];
484
+ const hasOnly = checkForOnly(rootSuite);
485
+
486
+ async function runSuite(suite, parentHooks, namePath) {
487
+ // Skip if this suite doesn't have any .only when .only exists elsewhere
488
+ if (hasOnly && !suiteHasOnly(suite)) return;
489
+
490
+ // Skip if suite is marked as skip
491
+ if (suite.skip) {
492
+ // Mark all tests in this suite as skipped
493
+ for (const t of suite.tests) {
494
+ results.push({
495
+ name: namePath ? namePath + ' > ' + t.name : t.name,
496
+ passed: true,
497
+ skipped: true,
498
+ duration: 0,
499
+ });
500
+ }
501
+ return;
502
+ }
503
+
504
+ // Run beforeAll hooks
505
+ for (const hook of suite.beforeAll) {
506
+ await hook();
507
+ }
508
+
509
+ // Run tests
510
+ for (const t of suite.tests) {
511
+ const testName = namePath ? namePath + ' > ' + t.name : t.name;
512
+
513
+ // Skip if .only is used and this test isn't .only AND the suite doesn't have .only
514
+ if (hasOnly && !t.only && !suite.only) continue;
515
+
516
+ // Skip if test is marked as skip
517
+ if (t.skip) {
518
+ results.push({
519
+ name: testName,
520
+ passed: true,
521
+ skipped: true,
522
+ duration: 0,
523
+ });
524
+ continue;
525
+ }
526
+
527
+ // Handle todo tests (no function provided)
528
+ if (t.todo) {
529
+ results.push({
530
+ name: testName,
531
+ passed: true,
532
+ skipped: true,
533
+ duration: 0,
534
+ });
535
+ continue;
536
+ }
537
+
538
+ const start = Date.now();
539
+ try {
540
+ // Run all beforeEach hooks (parent first, then current)
541
+ for (const hook of [...parentHooks.beforeEach, ...suite.beforeEach]) {
542
+ await hook();
543
+ }
544
+
545
+ // Run test
546
+ await t.fn();
547
+
548
+ // Run all afterEach hooks (current first, then parent)
549
+ for (const hook of [...suite.afterEach, ...parentHooks.afterEach]) {
550
+ await hook();
551
+ }
552
+
553
+ results.push({
554
+ name: testName,
555
+ passed: true,
556
+ duration: Date.now() - start,
557
+ });
558
+ } catch (err) {
559
+ results.push({
560
+ name: testName,
561
+ passed: false,
562
+ error: err.message || String(err),
563
+ duration: Date.now() - start,
564
+ });
565
+ }
566
+ }
567
+
568
+ // Run child suites
569
+ for (const child of suite.children) {
570
+ const childPath = namePath ? namePath + ' > ' + child.name : child.name;
571
+ await runSuite(child, {
572
+ beforeEach: [...parentHooks.beforeEach, ...suite.beforeEach],
573
+ afterEach: [...suite.afterEach, ...parentHooks.afterEach],
574
+ }, childPath);
575
+ }
576
+
577
+ // Run afterAll hooks
578
+ for (const hook of suite.afterAll) {
579
+ await hook();
580
+ }
581
+ }
582
+
583
+ await runSuite(rootSuite, { beforeEach: [], afterEach: [] }, '');
584
+
585
+ const passed = results.filter(r => r.passed && !r.skipped).length;
586
+ const failed = results.filter(r => !r.passed).length;
587
+ const skipped = results.filter(r => r.skipped).length;
588
+
589
+ return JSON.stringify({
590
+ passed,
591
+ failed,
592
+ skipped,
593
+ total: results.length,
594
+ results,
595
+ });
596
+ }
597
+
598
+ // Reset function to clear state between runs
599
+ function __resetTestEnvironment() {
600
+ rootSuite.tests = [];
601
+ rootSuite.children = [];
602
+ rootSuite.beforeAll = [];
603
+ rootSuite.afterAll = [];
604
+ rootSuite.beforeEach = [];
605
+ rootSuite.afterEach = [];
606
+ currentSuite = rootSuite;
607
+ suiteStack.length = 0;
608
+ suiteStack.push(rootSuite);
609
+ }
610
+
611
+ // ============================================================
612
+ // Expose Globals
613
+ // ============================================================
614
+
615
+ globalThis.describe = describe;
616
+ globalThis.test = test;
617
+ globalThis.it = it;
618
+ globalThis.expect = expect;
619
+ globalThis.beforeEach = beforeEach;
620
+ globalThis.afterEach = afterEach;
621
+ globalThis.beforeAll = beforeAll;
622
+ globalThis.afterAll = afterAll;
623
+ globalThis.__runAllTests = __runAllTests;
624
+ globalThis.__resetTestEnvironment = __resetTestEnvironment;
625
+ })();
626
+ `;
627
+ async function setupTestEnvironment(context) {
628
+ context.evalSync(testEnvironmentCode);
629
+ return {
630
+ dispose() {
631
+ try {
632
+ context.evalSync("__resetTestEnvironment()");
633
+ } catch {}
634
+ }
635
+ };
636
+ }
637
+ async function runTests(context) {
638
+ const resultJson = await context.eval("__runAllTests()", { promise: true });
639
+ return JSON.parse(resultJson);
640
+ }
641
+ })
642
+
643
+ //# debugId=AE2FFE17A1C19B3664756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index.ts"],
4
+ "sourcesContent": [
5
+ "import type ivm from \"isolated-vm\";\n\nexport interface TestEnvironmentHandle {\n dispose(): void;\n}\n\nexport interface TestResults {\n passed: number;\n failed: number;\n total: number;\n results: TestResult[];\n}\n\nexport interface TestResult {\n name: string;\n passed: boolean;\n error?: string;\n duration: number;\n skipped?: boolean;\n}\n\nconst testEnvironmentCode = `\n(function() {\n // ============================================================\n // Internal State\n // ============================================================\n\n function createSuite(name, skip = false, only = false) {\n return {\n name,\n tests: [],\n children: [],\n beforeAll: [],\n afterAll: [],\n beforeEach: [],\n afterEach: [],\n skip,\n only,\n };\n }\n\n const rootSuite = createSuite('root');\n let currentSuite = rootSuite;\n const suiteStack = [rootSuite];\n\n // ============================================================\n // Deep Equality Helper\n // ============================================================\n\n function deepEqual(a, b) {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!deepEqual(a[key], b[key])) return false;\n }\n return true;\n }\n\n function strictDeepEqual(a, b) {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object' || a === null || b === null) return false;\n\n // Check prototypes\n if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;\n\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n\n // For arrays, check sparse arrays (holes)\n if (Array.isArray(a)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const aHasIndex = i in a;\n const bHasIndex = i in b;\n if (aHasIndex !== bHasIndex) return false;\n if (aHasIndex && !strictDeepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Check for undefined properties vs missing properties\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!keysB.includes(key)) return false;\n if (!strictDeepEqual(a[key], b[key])) return false;\n }\n\n // Check for symbol properties\n const symbolsA = Object.getOwnPropertySymbols(a);\n const symbolsB = Object.getOwnPropertySymbols(b);\n if (symbolsA.length !== symbolsB.length) return false;\n\n for (const sym of symbolsA) {\n if (!symbolsB.includes(sym)) return false;\n if (!strictDeepEqual(a[sym], b[sym])) return false;\n }\n\n return true;\n }\n\n function getNestedProperty(obj, path) {\n const parts = path.split('.');\n let current = obj;\n for (const part of parts) {\n if (current == null || !(part in current)) {\n return { exists: false };\n }\n current = current[part];\n }\n return { exists: true, value: current };\n }\n\n function formatValue(val) {\n if (val === null) return 'null';\n if (val === undefined) return 'undefined';\n if (typeof val === 'string') return JSON.stringify(val);\n if (typeof val === 'object') {\n try {\n return JSON.stringify(val);\n } catch {\n return String(val);\n }\n }\n return String(val);\n }\n\n // ============================================================\n // expect() Implementation\n // ============================================================\n\n function expect(actual) {\n function createMatchers(negated = false) {\n const assert = (condition, message) => {\n const pass = negated ? !condition : condition;\n if (!pass) {\n throw new Error(message);\n }\n };\n\n const matchers = {\n toBe(expected) {\n assert(\n actual === expected,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to be \\${formatValue(expected)}\\`\n );\n },\n\n toEqual(expected) {\n assert(\n deepEqual(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to equal \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to equal \\${formatValue(expected)}\\`\n );\n },\n\n toStrictEqual(expected) {\n assert(\n strictDeepEqual(actual, expected),\n negated\n ? \\`Expected \\${formatValue(actual)} not to strictly equal \\${formatValue(expected)}\\`\n : \\`Expected \\${formatValue(actual)} to strictly equal \\${formatValue(expected)}\\`\n );\n },\n\n toBeTruthy() {\n assert(\n !!actual,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be truthy\\`\n : \\`Expected \\${formatValue(actual)} to be truthy\\`\n );\n },\n\n toBeFalsy() {\n assert(\n !actual,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be falsy\\`\n : \\`Expected \\${formatValue(actual)} to be falsy\\`\n );\n },\n\n toBeNull() {\n assert(\n actual === null,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be null\\`\n : \\`Expected \\${formatValue(actual)} to be null\\`\n );\n },\n\n toBeUndefined() {\n assert(\n actual === undefined,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be undefined\\`\n : \\`Expected \\${formatValue(actual)} to be undefined\\`\n );\n },\n\n toBeDefined() {\n assert(\n actual !== undefined,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be defined\\`\n : \\`Expected \\${formatValue(actual)} to be defined\\`\n );\n },\n\n toContain(item) {\n let contains = false;\n if (Array.isArray(actual)) {\n contains = actual.includes(item);\n } else if (typeof actual === 'string') {\n contains = actual.includes(item);\n }\n assert(\n contains,\n negated\n ? \\`Expected \\${formatValue(actual)} not to contain \\${formatValue(item)}\\`\n : \\`Expected \\${formatValue(actual)} to contain \\${formatValue(item)}\\`\n );\n },\n\n toThrow(expected) {\n if (typeof actual !== 'function') {\n throw new Error('toThrow requires a function');\n }\n\n let threw = false;\n let error = null;\n try {\n actual();\n } catch (e) {\n threw = true;\n error = e;\n }\n\n if (expected !== undefined) {\n const matches = threw && (\n (typeof expected === 'string' && error.message.includes(expected)) ||\n (expected instanceof RegExp && expected.test(error.message)) ||\n (typeof expected === 'function' && error instanceof expected)\n );\n assert(\n matches,\n negated\n ? \\`Expected function not to throw \\${formatValue(expected)}\\`\n : \\`Expected function to throw \\${formatValue(expected)}, but \\${threw ? \\`threw: \\${error.message}\\` : 'did not throw'}\\`\n );\n } else {\n assert(\n threw,\n negated\n ? \\`Expected function not to throw\\`\n : \\`Expected function to throw\\`\n );\n }\n },\n\n toBeInstanceOf(cls) {\n assert(\n actual instanceof cls,\n negated\n ? \\`Expected \\${formatValue(actual)} not to be instance of \\${cls.name || cls}\\`\n : \\`Expected \\${formatValue(actual)} to be instance of \\${cls.name || cls}\\`\n );\n },\n\n toHaveLength(length) {\n const actualLength = actual?.length;\n assert(\n actualLength === length,\n negated\n ? \\`Expected length not to be \\${length}, but got \\${actualLength}\\`\n : \\`Expected length to be \\${length}, but got \\${actualLength}\\`\n );\n },\n\n toMatch(pattern) {\n let matches = false;\n if (typeof pattern === 'string') {\n matches = actual.includes(pattern);\n } else if (pattern instanceof RegExp) {\n matches = pattern.test(actual);\n }\n assert(\n matches,\n negated\n ? \\`Expected \\${formatValue(actual)} not to match \\${pattern}\\`\n : \\`Expected \\${formatValue(actual)} to match \\${pattern}\\`\n );\n },\n\n toHaveProperty(path, value) {\n const prop = getNestedProperty(actual, path);\n const hasProperty = prop.exists;\n const valueMatches = arguments.length < 2 || deepEqual(prop.value, value);\n\n assert(\n hasProperty && valueMatches,\n negated\n ? \\`Expected \\${formatValue(actual)} not to have property \\${path}\\${arguments.length >= 2 ? \\` with value \\${formatValue(value)}\\` : ''}\\`\n : \\`Expected \\${formatValue(actual)} to have property \\${path}\\${arguments.length >= 2 ? \\` with value \\${formatValue(value)}\\` : ''}\\`\n );\n },\n };\n\n return matchers;\n }\n\n const matchers = createMatchers(false);\n matchers.not = createMatchers(true);\n\n return matchers;\n }\n\n // ============================================================\n // Test Registration Functions\n // ============================================================\n\n function describe(name, fn) {\n const suite = createSuite(name);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n }\n\n describe.skip = function(name, fn) {\n const suite = createSuite(name, true, false);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n };\n\n describe.only = function(name, fn) {\n const suite = createSuite(name, false, true);\n currentSuite.children.push(suite);\n\n const parentSuite = currentSuite;\n currentSuite = suite;\n suiteStack.push(suite);\n\n fn();\n\n suiteStack.pop();\n currentSuite = parentSuite;\n };\n\n function test(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: false,\n only: false,\n });\n }\n\n test.skip = function(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: true,\n only: false,\n });\n };\n\n test.only = function(name, fn) {\n currentSuite.tests.push({\n name,\n fn,\n skip: false,\n only: true,\n });\n };\n\n test.todo = function(name) {\n currentSuite.tests.push({\n name,\n fn: null,\n skip: false,\n only: false,\n todo: true,\n });\n };\n\n const it = test;\n it.skip = test.skip;\n it.only = test.only;\n it.todo = test.todo;\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n function beforeEach(fn) {\n currentSuite.beforeEach.push(fn);\n }\n\n function afterEach(fn) {\n currentSuite.afterEach.push(fn);\n }\n\n function beforeAll(fn) {\n currentSuite.beforeAll.push(fn);\n }\n\n function afterAll(fn) {\n currentSuite.afterAll.push(fn);\n }\n\n // ============================================================\n // Test Runner\n // ============================================================\n\n function checkForOnly(suite) {\n if (suite.only) return true;\n for (const t of suite.tests) {\n if (t.only) return true;\n }\n for (const child of suite.children) {\n if (checkForOnly(child)) return true;\n }\n return false;\n }\n\n function suiteHasOnly(suite) {\n if (suite.only) return true;\n for (const t of suite.tests) {\n if (t.only) return true;\n }\n for (const child of suite.children) {\n if (suiteHasOnly(child)) return true;\n }\n return false;\n }\n\n async function __runAllTests() {\n const results = [];\n const hasOnly = checkForOnly(rootSuite);\n\n async function runSuite(suite, parentHooks, namePath) {\n // Skip if this suite doesn't have any .only when .only exists elsewhere\n if (hasOnly && !suiteHasOnly(suite)) return;\n\n // Skip if suite is marked as skip\n if (suite.skip) {\n // Mark all tests in this suite as skipped\n for (const t of suite.tests) {\n results.push({\n name: namePath ? namePath + ' > ' + t.name : t.name,\n passed: true,\n skipped: true,\n duration: 0,\n });\n }\n return;\n }\n\n // Run beforeAll hooks\n for (const hook of suite.beforeAll) {\n await hook();\n }\n\n // Run tests\n for (const t of suite.tests) {\n const testName = namePath ? namePath + ' > ' + t.name : t.name;\n\n // Skip if .only is used and this test isn't .only AND the suite doesn't have .only\n if (hasOnly && !t.only && !suite.only) continue;\n\n // Skip if test is marked as skip\n if (t.skip) {\n results.push({\n name: testName,\n passed: true,\n skipped: true,\n duration: 0,\n });\n continue;\n }\n\n // Handle todo tests (no function provided)\n if (t.todo) {\n results.push({\n name: testName,\n passed: true,\n skipped: true,\n duration: 0,\n });\n continue;\n }\n\n const start = Date.now();\n try {\n // Run all beforeEach hooks (parent first, then current)\n for (const hook of [...parentHooks.beforeEach, ...suite.beforeEach]) {\n await hook();\n }\n\n // Run test\n await t.fn();\n\n // Run all afterEach hooks (current first, then parent)\n for (const hook of [...suite.afterEach, ...parentHooks.afterEach]) {\n await hook();\n }\n\n results.push({\n name: testName,\n passed: true,\n duration: Date.now() - start,\n });\n } catch (err) {\n results.push({\n name: testName,\n passed: false,\n error: err.message || String(err),\n duration: Date.now() - start,\n });\n }\n }\n\n // Run child suites\n for (const child of suite.children) {\n const childPath = namePath ? namePath + ' > ' + child.name : child.name;\n await runSuite(child, {\n beforeEach: [...parentHooks.beforeEach, ...suite.beforeEach],\n afterEach: [...suite.afterEach, ...parentHooks.afterEach],\n }, childPath);\n }\n\n // Run afterAll hooks\n for (const hook of suite.afterAll) {\n await hook();\n }\n }\n\n await runSuite(rootSuite, { beforeEach: [], afterEach: [] }, '');\n\n const passed = results.filter(r => r.passed && !r.skipped).length;\n const failed = results.filter(r => !r.passed).length;\n const skipped = results.filter(r => r.skipped).length;\n\n return JSON.stringify({\n passed,\n failed,\n skipped,\n total: results.length,\n results,\n });\n }\n\n // Reset function to clear state between runs\n function __resetTestEnvironment() {\n rootSuite.tests = [];\n rootSuite.children = [];\n rootSuite.beforeAll = [];\n rootSuite.afterAll = [];\n rootSuite.beforeEach = [];\n rootSuite.afterEach = [];\n currentSuite = rootSuite;\n suiteStack.length = 0;\n suiteStack.push(rootSuite);\n }\n\n // ============================================================\n // Expose Globals\n // ============================================================\n\n globalThis.describe = describe;\n globalThis.test = test;\n globalThis.it = it;\n globalThis.expect = expect;\n globalThis.beforeEach = beforeEach;\n globalThis.afterEach = afterEach;\n globalThis.beforeAll = beforeAll;\n globalThis.afterAll = afterAll;\n globalThis.__runAllTests = __runAllTests;\n globalThis.__resetTestEnvironment = __resetTestEnvironment;\n})();\n`;\n\n/**\n * Setup test environment primitives in an isolated-vm context\n *\n * Provides Jest/Vitest-compatible test primitives:\n * - describe, test, it\n * - beforeEach, afterEach, beforeAll, afterAll\n * - expect matchers\n *\n * @example\n * const handle = await setupTestEnvironment(context);\n *\n * await context.eval(`\n * describe(\"my tests\", () => {\n * test(\"example\", () => {\n * expect(1 + 1).toBe(2);\n * });\n * });\n * `);\n */\nexport async function setupTestEnvironment(\n context: ivm.Context\n): Promise<TestEnvironmentHandle> {\n context.evalSync(testEnvironmentCode);\n\n return {\n dispose() {\n // Reset the test environment state\n try {\n context.evalSync(\"__resetTestEnvironment()\");\n } catch {\n // Context may already be released\n }\n },\n };\n}\n\n/**\n * Run tests in the context and return results\n */\nexport async function runTests(context: ivm.Context): Promise<TestResults> {\n const resultJson = await context.eval(\"__runAllTests()\", { promise: true });\n return JSON.parse(resultJson as string);\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkmB5B,eAAsB,oBAAoB,CACxC,SACgC;AAAA,EAChC,QAAQ,SAAS,mBAAmB;AAAA,EAEpC,OAAO;AAAA,IACL,OAAO,GAAG;AAAA,MAER,IAAI;AAAA,QACF,QAAQ,SAAS,0BAA0B;AAAA,QAC3C,MAAM;AAAA;AAAA,EAIZ;AAAA;AAMF,eAAsB,QAAQ,CAAC,SAA4C;AAAA,EACzE,MAAM,aAAa,MAAM,QAAQ,KAAK,mBAAmB,EAAE,SAAS,KAAK,CAAC;AAAA,EAC1E,OAAO,KAAK,MAAM,UAAoB;AAAA;",
8
+ "debugId": "AE2FFE17A1C19B3664756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@ricsam/isolate-test-environment",
3
+ "version": "0.1.2",
4
+ "type": "commonjs"
5
+ }