@ricsam/isolate-test-environment 0.1.11 → 0.1.13
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/dist/cjs/index.cjs +458 -3
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/index.mjs +458 -3
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/package.json +1 -1
package/dist/mjs/index.mjs
CHANGED
|
@@ -6,6 +6,26 @@ var testEnvironmentCode = `
|
|
|
6
6
|
// Internal State
|
|
7
7
|
// ============================================================
|
|
8
8
|
|
|
9
|
+
// Mock registry and call counter
|
|
10
|
+
let __mockCallOrder = 0;
|
|
11
|
+
const __mockRegistry = [];
|
|
12
|
+
|
|
13
|
+
// Assertion counting state
|
|
14
|
+
let __expectedAssertions = null;
|
|
15
|
+
let __assertionCount = 0;
|
|
16
|
+
let __hasAssertionsFlag = false;
|
|
17
|
+
|
|
18
|
+
function createMockState() {
|
|
19
|
+
return {
|
|
20
|
+
calls: [],
|
|
21
|
+
results: [],
|
|
22
|
+
contexts: [],
|
|
23
|
+
instances: [],
|
|
24
|
+
invocationCallOrder: [],
|
|
25
|
+
lastCall: undefined,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
9
29
|
function createSuite(name, skip = false, only = false) {
|
|
10
30
|
return {
|
|
11
31
|
name,
|
|
@@ -51,6 +71,34 @@ var testEnvironmentCode = `
|
|
|
51
71
|
}
|
|
52
72
|
}
|
|
53
73
|
|
|
74
|
+
// ============================================================
|
|
75
|
+
// Asymmetric Matcher Infrastructure
|
|
76
|
+
// ============================================================
|
|
77
|
+
|
|
78
|
+
const ASYMMETRIC_MATCHER = Symbol('asymmetricMatcher');
|
|
79
|
+
|
|
80
|
+
function isAsymmetricMatcher(obj) {
|
|
81
|
+
return obj && obj[ASYMMETRIC_MATCHER] === true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Deep equality with asymmetric matcher support
|
|
85
|
+
function asymmetricDeepEqual(a, b) {
|
|
86
|
+
if (isAsymmetricMatcher(b)) return b.asymmetricMatch(a);
|
|
87
|
+
if (isAsymmetricMatcher(a)) return a.asymmetricMatch(b);
|
|
88
|
+
if (a === b) return true;
|
|
89
|
+
if (typeof a !== typeof b) return false;
|
|
90
|
+
if (typeof a !== 'object' || a === null || b === null) return false;
|
|
91
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
92
|
+
const keysA = Object.keys(a);
|
|
93
|
+
const keysB = Object.keys(b);
|
|
94
|
+
if (keysA.length !== keysB.length) return false;
|
|
95
|
+
for (const key of keysA) {
|
|
96
|
+
if (!keysB.includes(key)) return false;
|
|
97
|
+
if (!asymmetricDeepEqual(a[key], b[key])) return false;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
54
102
|
// ============================================================
|
|
55
103
|
// Deep Equality Helper
|
|
56
104
|
// ============================================================
|
|
@@ -151,6 +199,7 @@ var testEnvironmentCode = `
|
|
|
151
199
|
function expect(actual) {
|
|
152
200
|
function createMatchers(negated = false) {
|
|
153
201
|
const assert = (condition, message, matcherName, expected) => {
|
|
202
|
+
__assertionCount++;
|
|
154
203
|
const pass = negated ? !condition : condition;
|
|
155
204
|
if (!pass) {
|
|
156
205
|
throw new TestError(message, matcherName, expected, actual);
|
|
@@ -171,7 +220,7 @@ var testEnvironmentCode = `
|
|
|
171
220
|
|
|
172
221
|
toEqual(expected) {
|
|
173
222
|
assert(
|
|
174
|
-
|
|
223
|
+
asymmetricDeepEqual(actual, expected),
|
|
175
224
|
negated
|
|
176
225
|
? \`Expected \${formatValue(actual)} not to equal \${formatValue(expected)}\`
|
|
177
226
|
: \`Expected \${formatValue(actual)} to equal \${formatValue(expected)}\`,
|
|
@@ -346,7 +395,7 @@ var testEnvironmentCode = `
|
|
|
346
395
|
toHaveProperty(path, value) {
|
|
347
396
|
const prop = getNestedProperty(actual, path);
|
|
348
397
|
const hasProperty = prop.exists;
|
|
349
|
-
const valueMatches = arguments.length < 2 ||
|
|
398
|
+
const valueMatches = arguments.length < 2 || asymmetricDeepEqual(prop.value, value);
|
|
350
399
|
|
|
351
400
|
assert(
|
|
352
401
|
hasProperty && valueMatches,
|
|
@@ -401,6 +450,163 @@ var testEnvironmentCode = `
|
|
|
401
450
|
expected
|
|
402
451
|
);
|
|
403
452
|
},
|
|
453
|
+
|
|
454
|
+
toBeCloseTo(expected, numDigits = 2) {
|
|
455
|
+
const precision = Math.pow(10, -numDigits) / 2;
|
|
456
|
+
const pass = Math.abs(actual - expected) < precision;
|
|
457
|
+
assert(
|
|
458
|
+
pass,
|
|
459
|
+
negated
|
|
460
|
+
? \`Expected \${formatValue(actual)} not to be close to \${formatValue(expected)} (precision: \${numDigits} digits)\`
|
|
461
|
+
: \`Expected \${formatValue(actual)} to be close to \${formatValue(expected)} (precision: \${numDigits} digits)\`,
|
|
462
|
+
'toBeCloseTo',
|
|
463
|
+
expected
|
|
464
|
+
);
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
toBeNaN() {
|
|
468
|
+
assert(
|
|
469
|
+
Number.isNaN(actual),
|
|
470
|
+
negated
|
|
471
|
+
? \`Expected \${formatValue(actual)} not to be NaN\`
|
|
472
|
+
: \`Expected \${formatValue(actual)} to be NaN\`,
|
|
473
|
+
'toBeNaN',
|
|
474
|
+
NaN
|
|
475
|
+
);
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
toMatchObject(expected) {
|
|
479
|
+
function matchesObject(obj, pattern) {
|
|
480
|
+
if (isAsymmetricMatcher(pattern)) return pattern.asymmetricMatch(obj);
|
|
481
|
+
if (typeof pattern !== 'object' || pattern === null) return obj === pattern;
|
|
482
|
+
if (typeof obj !== 'object' || obj === null) return false;
|
|
483
|
+
for (const key of Object.keys(pattern)) {
|
|
484
|
+
if (!(key in obj)) return false;
|
|
485
|
+
if (!matchesObject(obj[key], pattern[key])) return false;
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
assert(
|
|
490
|
+
matchesObject(actual, expected),
|
|
491
|
+
negated
|
|
492
|
+
? \`Expected \${formatValue(actual)} not to match object \${formatValue(expected)}\`
|
|
493
|
+
: \`Expected \${formatValue(actual)} to match object \${formatValue(expected)}\`,
|
|
494
|
+
'toMatchObject',
|
|
495
|
+
expected
|
|
496
|
+
);
|
|
497
|
+
},
|
|
498
|
+
|
|
499
|
+
toContainEqual(item) {
|
|
500
|
+
const contains = Array.isArray(actual) && actual.some(el => asymmetricDeepEqual(el, item));
|
|
501
|
+
assert(
|
|
502
|
+
contains,
|
|
503
|
+
negated
|
|
504
|
+
? \`Expected array not to contain equal \${formatValue(item)}\`
|
|
505
|
+
: \`Expected array to contain equal \${formatValue(item)}\`,
|
|
506
|
+
'toContainEqual',
|
|
507
|
+
item
|
|
508
|
+
);
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
toBeTypeOf(expectedType) {
|
|
512
|
+
const actualType = typeof actual;
|
|
513
|
+
assert(
|
|
514
|
+
actualType === expectedType,
|
|
515
|
+
negated
|
|
516
|
+
? \`Expected typeof \${formatValue(actual)} not to be "\${expectedType}"\`
|
|
517
|
+
: \`Expected typeof \${formatValue(actual)} to be "\${expectedType}", got "\${actualType}"\`,
|
|
518
|
+
'toBeTypeOf',
|
|
519
|
+
expectedType
|
|
520
|
+
);
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
// Mock matchers
|
|
524
|
+
toHaveBeenCalled() {
|
|
525
|
+
if (!actual.__isMockFunction) throw new Error('toHaveBeenCalled requires a mock function');
|
|
526
|
+
assert(actual.mock.calls.length > 0,
|
|
527
|
+
negated ? \`Expected mock not to have been called\` : \`Expected mock to have been called\`,
|
|
528
|
+
'toHaveBeenCalled', 'called');
|
|
529
|
+
},
|
|
530
|
+
|
|
531
|
+
toHaveBeenCalledTimes(n) {
|
|
532
|
+
if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledTimes requires a mock function');
|
|
533
|
+
assert(actual.mock.calls.length === n,
|
|
534
|
+
negated ? \`Expected mock not to have been called \${n} times\`
|
|
535
|
+
: \`Expected mock to be called \${n} times, got \${actual.mock.calls.length}\`,
|
|
536
|
+
'toHaveBeenCalledTimes', n);
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
toHaveBeenCalledWith(...expectedArgs) {
|
|
540
|
+
if (!actual.__isMockFunction) throw new Error('toHaveBeenCalledWith requires a mock function');
|
|
541
|
+
const match = actual.mock.calls.some(args => asymmetricDeepEqual(args, expectedArgs));
|
|
542
|
+
assert(match,
|
|
543
|
+
negated ? \`Expected mock not to have been called with \${formatValue(expectedArgs)}\`
|
|
544
|
+
: \`Expected mock to have been called with \${formatValue(expectedArgs)}\`,
|
|
545
|
+
'toHaveBeenCalledWith', expectedArgs);
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
toHaveBeenLastCalledWith(...expectedArgs) {
|
|
549
|
+
if (!actual.__isMockFunction) throw new Error('toHaveBeenLastCalledWith requires a mock function');
|
|
550
|
+
assert(actual.mock.lastCall && asymmetricDeepEqual(actual.mock.lastCall, expectedArgs),
|
|
551
|
+
negated ? \`Expected last call not to be \${formatValue(expectedArgs)}\`
|
|
552
|
+
: \`Expected last call to be \${formatValue(expectedArgs)}, got \${formatValue(actual.mock.lastCall)}\`,
|
|
553
|
+
'toHaveBeenLastCalledWith', expectedArgs);
|
|
554
|
+
},
|
|
555
|
+
|
|
556
|
+
toHaveBeenNthCalledWith(n, ...expectedArgs) {
|
|
557
|
+
if (!actual.__isMockFunction) throw new Error('toHaveBeenNthCalledWith requires a mock function');
|
|
558
|
+
const nthCall = actual.mock.calls[n - 1];
|
|
559
|
+
assert(nthCall && asymmetricDeepEqual(nthCall, expectedArgs),
|
|
560
|
+
negated ? \`Expected call \${n} not to be \${formatValue(expectedArgs)}\`
|
|
561
|
+
: \`Expected call \${n} to be \${formatValue(expectedArgs)}, got \${formatValue(nthCall)}\`,
|
|
562
|
+
'toHaveBeenNthCalledWith', expectedArgs);
|
|
563
|
+
},
|
|
564
|
+
|
|
565
|
+
toHaveReturned() {
|
|
566
|
+
if (!actual.__isMockFunction) throw new Error('toHaveReturned requires a mock function');
|
|
567
|
+
const hasReturned = actual.mock.results.some(r => r.type === 'return');
|
|
568
|
+
assert(hasReturned,
|
|
569
|
+
negated ? \`Expected mock not to have returned\` : \`Expected mock to have returned\`,
|
|
570
|
+
'toHaveReturned', 'returned');
|
|
571
|
+
},
|
|
572
|
+
|
|
573
|
+
toHaveReturnedWith(value) {
|
|
574
|
+
if (!actual.__isMockFunction) throw new Error('toHaveReturnedWith requires a mock function');
|
|
575
|
+
const match = actual.mock.results.some(r => r.type === 'return' && asymmetricDeepEqual(r.value, value));
|
|
576
|
+
assert(match,
|
|
577
|
+
negated ? \`Expected mock not to have returned \${formatValue(value)}\`
|
|
578
|
+
: \`Expected mock to have returned \${formatValue(value)}\`,
|
|
579
|
+
'toHaveReturnedWith', value);
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
toHaveLastReturnedWith(value) {
|
|
583
|
+
if (!actual.__isMockFunction) throw new Error('toHaveLastReturnedWith requires a mock function');
|
|
584
|
+
const returns = actual.mock.results.filter(r => r.type === 'return');
|
|
585
|
+
const last = returns[returns.length - 1];
|
|
586
|
+
assert(last && asymmetricDeepEqual(last.value, value),
|
|
587
|
+
negated ? \`Expected last return not to be \${formatValue(value)}\`
|
|
588
|
+
: \`Expected last return to be \${formatValue(value)}, got \${formatValue(last?.value)}\`,
|
|
589
|
+
'toHaveLastReturnedWith', value);
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
toHaveReturnedTimes(n) {
|
|
593
|
+
if (!actual.__isMockFunction) throw new Error('toHaveReturnedTimes requires a mock function');
|
|
594
|
+
const returnCount = actual.mock.results.filter(r => r.type === 'return').length;
|
|
595
|
+
assert(returnCount === n,
|
|
596
|
+
negated ? \`Expected mock not to have returned \${n} times\`
|
|
597
|
+
: \`Expected mock to have returned \${n} times, got \${returnCount}\`,
|
|
598
|
+
'toHaveReturnedTimes', n);
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
toHaveNthReturnedWith(n, value) {
|
|
602
|
+
if (!actual.__isMockFunction) throw new Error('toHaveNthReturnedWith requires a mock function');
|
|
603
|
+
const returns = actual.mock.results.filter(r => r.type === 'return');
|
|
604
|
+
const nthReturn = returns[n - 1];
|
|
605
|
+
assert(nthReturn && asymmetricDeepEqual(nthReturn.value, value),
|
|
606
|
+
negated ? \`Expected return \${n} not to be \${formatValue(value)}\`
|
|
607
|
+
: \`Expected return \${n} to be \${formatValue(value)}, got \${formatValue(nthReturn?.value)}\`,
|
|
608
|
+
'toHaveNthReturnedWith', value);
|
|
609
|
+
},
|
|
404
610
|
};
|
|
405
611
|
|
|
406
612
|
return matchers;
|
|
@@ -409,9 +615,129 @@ var testEnvironmentCode = `
|
|
|
409
615
|
const matchers = createMatchers(false);
|
|
410
616
|
matchers.not = createMatchers(true);
|
|
411
617
|
|
|
618
|
+
// Promise matchers using Proxy
|
|
619
|
+
matchers.resolves = new Proxy({}, {
|
|
620
|
+
get(_, matcherName) {
|
|
621
|
+
if (matcherName === 'not') {
|
|
622
|
+
return new Proxy({}, {
|
|
623
|
+
get(_, negatedMatcherName) {
|
|
624
|
+
return async (...args) => {
|
|
625
|
+
const result = await actual;
|
|
626
|
+
return expect(result).not[negatedMatcherName](...args);
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return async (...args) => {
|
|
632
|
+
const result = await actual;
|
|
633
|
+
return expect(result)[matcherName](...args);
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
matchers.rejects = new Proxy({}, {
|
|
639
|
+
get(_, matcherName) {
|
|
640
|
+
if (matcherName === 'not') {
|
|
641
|
+
return new Proxy({}, {
|
|
642
|
+
get(_, negatedMatcherName) {
|
|
643
|
+
return async (...args) => {
|
|
644
|
+
let error;
|
|
645
|
+
try {
|
|
646
|
+
await actual;
|
|
647
|
+
throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);
|
|
648
|
+
} catch (e) {
|
|
649
|
+
if (e instanceof TestError && e.matcherName === 'rejects') throw e;
|
|
650
|
+
error = e;
|
|
651
|
+
}
|
|
652
|
+
return expect(error).not[negatedMatcherName](...args);
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
return async (...args) => {
|
|
658
|
+
let error;
|
|
659
|
+
try {
|
|
660
|
+
await actual;
|
|
661
|
+
throw new TestError('Expected promise to reject', 'rejects', 'rejection', undefined);
|
|
662
|
+
} catch (e) {
|
|
663
|
+
if (e instanceof TestError && e.matcherName === 'rejects') throw e;
|
|
664
|
+
error = e;
|
|
665
|
+
}
|
|
666
|
+
return expect(error)[matcherName](...args);
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
|
|
412
671
|
return matchers;
|
|
413
672
|
}
|
|
414
673
|
|
|
674
|
+
// Asymmetric matcher implementations
|
|
675
|
+
expect.anything = () => ({
|
|
676
|
+
[ASYMMETRIC_MATCHER]: true,
|
|
677
|
+
asymmetricMatch: (other) => other !== null && other !== undefined,
|
|
678
|
+
toString: () => 'anything()',
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
expect.any = (constructor) => ({
|
|
682
|
+
[ASYMMETRIC_MATCHER]: true,
|
|
683
|
+
asymmetricMatch: (other) => {
|
|
684
|
+
if (constructor === String) return typeof other === 'string' || other instanceof String;
|
|
685
|
+
if (constructor === Number) return typeof other === 'number' || other instanceof Number;
|
|
686
|
+
if (constructor === Boolean) return typeof other === 'boolean' || other instanceof Boolean;
|
|
687
|
+
if (constructor === Function) return typeof other === 'function';
|
|
688
|
+
if (constructor === Object) return typeof other === 'object' && other !== null;
|
|
689
|
+
if (constructor === Array) return Array.isArray(other);
|
|
690
|
+
return other instanceof constructor;
|
|
691
|
+
},
|
|
692
|
+
toString: () => \`any(\${constructor.name || constructor})\`,
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
expect.stringContaining = (str) => ({
|
|
696
|
+
[ASYMMETRIC_MATCHER]: true,
|
|
697
|
+
asymmetricMatch: (other) => typeof other === 'string' && other.includes(str),
|
|
698
|
+
toString: () => \`stringContaining("\${str}")\`,
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
expect.stringMatching = (pattern) => ({
|
|
702
|
+
[ASYMMETRIC_MATCHER]: true,
|
|
703
|
+
asymmetricMatch: (other) => {
|
|
704
|
+
if (typeof other !== 'string') return false;
|
|
705
|
+
return typeof pattern === 'string' ? other.includes(pattern) : pattern.test(other);
|
|
706
|
+
},
|
|
707
|
+
toString: () => \`stringMatching(\${pattern})\`,
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
expect.arrayContaining = (expected) => ({
|
|
711
|
+
[ASYMMETRIC_MATCHER]: true,
|
|
712
|
+
asymmetricMatch: (other) => {
|
|
713
|
+
if (!Array.isArray(other)) return false;
|
|
714
|
+
return expected.every(exp => other.some(item => asymmetricDeepEqual(item, exp)));
|
|
715
|
+
},
|
|
716
|
+
toString: () => \`arrayContaining(\${formatValue(expected)})\`,
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
expect.objectContaining = (expected) => ({
|
|
720
|
+
[ASYMMETRIC_MATCHER]: true,
|
|
721
|
+
asymmetricMatch: (other) => {
|
|
722
|
+
if (typeof other !== 'object' || other === null) return false;
|
|
723
|
+
for (const key of Object.keys(expected)) {
|
|
724
|
+
if (!(key in other)) return false;
|
|
725
|
+
if (!asymmetricDeepEqual(other[key], expected[key])) return false;
|
|
726
|
+
}
|
|
727
|
+
return true;
|
|
728
|
+
},
|
|
729
|
+
toString: () => \`objectContaining(\${formatValue(expected)})\`,
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// Assertion counting
|
|
733
|
+
expect.assertions = (n) => {
|
|
734
|
+
__expectedAssertions = n;
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
expect.hasAssertions = () => {
|
|
738
|
+
__hasAssertionsFlag = true;
|
|
739
|
+
};
|
|
740
|
+
|
|
415
741
|
// ============================================================
|
|
416
742
|
// Test Registration Functions
|
|
417
743
|
// ============================================================
|
|
@@ -520,6 +846,113 @@ var testEnvironmentCode = `
|
|
|
520
846
|
currentSuite.afterAll.push(fn);
|
|
521
847
|
}
|
|
522
848
|
|
|
849
|
+
// ============================================================
|
|
850
|
+
// Mock Implementation
|
|
851
|
+
// ============================================================
|
|
852
|
+
|
|
853
|
+
const mock = {
|
|
854
|
+
fn(implementation) {
|
|
855
|
+
const mockState = createMockState();
|
|
856
|
+
let defaultImpl = implementation;
|
|
857
|
+
const onceImpls = [];
|
|
858
|
+
const onceReturns = [];
|
|
859
|
+
let returnVal, resolvedVal, rejectedVal;
|
|
860
|
+
let returnSet = false, resolvedSet = false, rejectedSet = false;
|
|
861
|
+
|
|
862
|
+
function mockFn(...args) {
|
|
863
|
+
mockState.calls.push(args);
|
|
864
|
+
mockState.contexts.push(this);
|
|
865
|
+
mockState.lastCall = args;
|
|
866
|
+
mockState.invocationCallOrder.push(++__mockCallOrder);
|
|
867
|
+
|
|
868
|
+
let result;
|
|
869
|
+
try {
|
|
870
|
+
if (onceImpls.length > 0) {
|
|
871
|
+
result = onceImpls.shift().apply(this, args);
|
|
872
|
+
} else if (onceReturns.length > 0) {
|
|
873
|
+
result = onceReturns.shift();
|
|
874
|
+
} else if (returnSet) {
|
|
875
|
+
result = returnVal;
|
|
876
|
+
} else if (resolvedSet) {
|
|
877
|
+
result = Promise.resolve(resolvedVal);
|
|
878
|
+
} else if (rejectedSet) {
|
|
879
|
+
result = Promise.reject(rejectedVal);
|
|
880
|
+
} else if (defaultImpl) {
|
|
881
|
+
result = defaultImpl.apply(this, args);
|
|
882
|
+
}
|
|
883
|
+
mockState.results.push({ type: 'return', value: result });
|
|
884
|
+
return result;
|
|
885
|
+
} catch (e) {
|
|
886
|
+
mockState.results.push({ type: 'throw', value: e });
|
|
887
|
+
throw e;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
mockFn.__isMockFunction = true;
|
|
892
|
+
mockFn.mock = mockState;
|
|
893
|
+
|
|
894
|
+
// Configuration methods
|
|
895
|
+
mockFn.mockReturnValue = (v) => { returnVal = v; returnSet = true; return mockFn; };
|
|
896
|
+
mockFn.mockReturnValueOnce = (v) => { onceReturns.push(v); return mockFn; };
|
|
897
|
+
mockFn.mockResolvedValue = (v) => { resolvedVal = v; resolvedSet = true; return mockFn; };
|
|
898
|
+
mockFn.mockRejectedValue = (v) => { rejectedVal = v; rejectedSet = true; return mockFn; };
|
|
899
|
+
mockFn.mockImplementation = (fn) => { defaultImpl = fn; return mockFn; };
|
|
900
|
+
mockFn.mockImplementationOnce = (fn) => { onceImpls.push(fn); return mockFn; };
|
|
901
|
+
|
|
902
|
+
// Clearing methods
|
|
903
|
+
mockFn.mockClear = () => {
|
|
904
|
+
mockState.calls = []; mockState.results = []; mockState.contexts = [];
|
|
905
|
+
mockState.instances = []; mockState.invocationCallOrder = []; mockState.lastCall = undefined;
|
|
906
|
+
return mockFn;
|
|
907
|
+
};
|
|
908
|
+
mockFn.mockReset = () => {
|
|
909
|
+
mockFn.mockClear();
|
|
910
|
+
defaultImpl = undefined; returnVal = resolvedVal = rejectedVal = undefined;
|
|
911
|
+
returnSet = resolvedSet = rejectedSet = false;
|
|
912
|
+
onceImpls.length = 0; onceReturns.length = 0;
|
|
913
|
+
return mockFn;
|
|
914
|
+
};
|
|
915
|
+
mockFn.mockRestore = () => mockFn.mockReset();
|
|
916
|
+
|
|
917
|
+
__mockRegistry.push(mockFn);
|
|
918
|
+
return mockFn;
|
|
919
|
+
},
|
|
920
|
+
|
|
921
|
+
spyOn(object, methodName) {
|
|
922
|
+
const original = object[methodName];
|
|
923
|
+
if (typeof original !== 'function') {
|
|
924
|
+
throw new Error(\`Cannot spy on \${methodName}: not a function\`);
|
|
925
|
+
}
|
|
926
|
+
const spy = mock.fn(original);
|
|
927
|
+
spy.__originalMethod = original;
|
|
928
|
+
spy.__spyTarget = object;
|
|
929
|
+
spy.__spyMethodName = methodName;
|
|
930
|
+
spy.__isSpyFunction = true;
|
|
931
|
+
spy.mockRestore = () => {
|
|
932
|
+
object[methodName] = original;
|
|
933
|
+
const idx = __mockRegistry.indexOf(spy);
|
|
934
|
+
if (idx !== -1) __mockRegistry.splice(idx, 1);
|
|
935
|
+
return spy;
|
|
936
|
+
};
|
|
937
|
+
object[methodName] = spy;
|
|
938
|
+
return spy;
|
|
939
|
+
},
|
|
940
|
+
|
|
941
|
+
clearAllMocks() {
|
|
942
|
+
for (const fn of __mockRegistry) fn.mockClear();
|
|
943
|
+
},
|
|
944
|
+
|
|
945
|
+
resetAllMocks() {
|
|
946
|
+
for (const fn of __mockRegistry) fn.mockReset();
|
|
947
|
+
},
|
|
948
|
+
|
|
949
|
+
restoreAllMocks() {
|
|
950
|
+
for (let i = __mockRegistry.length - 1; i >= 0; i--) {
|
|
951
|
+
if (__mockRegistry[i].__isSpyFunction) __mockRegistry[i].mockRestore();
|
|
952
|
+
}
|
|
953
|
+
},
|
|
954
|
+
};
|
|
955
|
+
|
|
523
956
|
// ============================================================
|
|
524
957
|
// Test Runner Helpers
|
|
525
958
|
// ============================================================
|
|
@@ -686,6 +1119,11 @@ var testEnvironmentCode = `
|
|
|
686
1119
|
}
|
|
687
1120
|
|
|
688
1121
|
const testStart = Date.now();
|
|
1122
|
+
// Reset assertion counting state before each test
|
|
1123
|
+
__expectedAssertions = null;
|
|
1124
|
+
__assertionCount = 0;
|
|
1125
|
+
__hasAssertionsFlag = false;
|
|
1126
|
+
|
|
689
1127
|
try {
|
|
690
1128
|
// Run all beforeEach hooks (parent first, then current)
|
|
691
1129
|
for (const hook of [...parentHooks.beforeEach, ...suite.beforeEach]) {
|
|
@@ -700,6 +1138,17 @@ var testEnvironmentCode = `
|
|
|
700
1138
|
await hook();
|
|
701
1139
|
}
|
|
702
1140
|
|
|
1141
|
+
// Verify assertion counts after test passes
|
|
1142
|
+
if (__hasAssertionsFlag && __assertionCount === 0) {
|
|
1143
|
+
throw new TestError('Expected at least one assertion', 'hasAssertions', '>0', 0);
|
|
1144
|
+
}
|
|
1145
|
+
if (__expectedAssertions !== null && __assertionCount !== __expectedAssertions) {
|
|
1146
|
+
throw new TestError(
|
|
1147
|
+
\`Expected \${__expectedAssertions} assertions, got \${__assertionCount}\`,
|
|
1148
|
+
'assertions', __expectedAssertions, __assertionCount
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
703
1152
|
const testResult = {
|
|
704
1153
|
name: t.name,
|
|
705
1154
|
suitePath: suitePath,
|
|
@@ -826,6 +1275,10 @@ var testEnvironmentCode = `
|
|
|
826
1275
|
currentSuite = rootSuite;
|
|
827
1276
|
suiteStack.length = 0;
|
|
828
1277
|
suiteStack.push(rootSuite);
|
|
1278
|
+
// Clear mocks
|
|
1279
|
+
__mockCallOrder = 0;
|
|
1280
|
+
mock.restoreAllMocks();
|
|
1281
|
+
__mockRegistry.length = 0;
|
|
829
1282
|
}
|
|
830
1283
|
|
|
831
1284
|
function __setEventCallback(callback) {
|
|
@@ -844,6 +1297,8 @@ var testEnvironmentCode = `
|
|
|
844
1297
|
globalThis.afterEach = afterEach;
|
|
845
1298
|
globalThis.beforeAll = beforeAll;
|
|
846
1299
|
globalThis.afterAll = afterAll;
|
|
1300
|
+
globalThis.mock = mock;
|
|
1301
|
+
globalThis.jest = mock; // Jest compatibility alias
|
|
847
1302
|
globalThis.__runAllTests = __runAllTests;
|
|
848
1303
|
globalThis.__resetTestEnvironment = __resetTestEnvironment;
|
|
849
1304
|
globalThis.__hasTests = __hasTests;
|
|
@@ -893,4 +1348,4 @@ export {
|
|
|
893
1348
|
getTestCount
|
|
894
1349
|
};
|
|
895
1350
|
|
|
896
|
-
//# debugId=
|
|
1351
|
+
//# debugId=2616F937BBAFD06664756E2164756E21
|