qunitx 1.2.6 → 1.2.8
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 +104 -20
- package/dist/.build-hash +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser/index.d.ts +20 -20
- package/dist/browser/index.js +52 -52
- package/dist/deno/index.d.ts +20 -19
- package/dist/deno/index.js +172 -186
- package/dist/deno/module.d.ts +2 -2
- package/dist/deno/module.js +11 -12
- package/dist/deno/test.d.ts +2 -2
- package/dist/deno/test.js +24 -25
- package/dist/node/index.d.ts +3 -2
- package/dist/node/index.js +1 -1
- package/dist/node/module.js +11 -12
- package/dist/node/test.js +23 -24
- package/dist/shared/assert.d.ts +2 -2
- package/dist/shared/assert.js +89 -55
- package/dist/shared/index.d.ts +1 -13
- package/dist/shared/index.js +11 -13
- package/dist/shared/module-context.d.ts +3 -3
- package/dist/shared/module-context.js +4 -4
- package/dist/shared/test-context.d.ts +10 -18
- package/dist/shared/test-context.js +26 -74
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
- package/shims/deno/index.ts +22 -21
- package/shims/deno/module.ts +15 -15
- package/shims/deno/test.ts +30 -29
- package/shims/shared/assert.ts +92 -56
- package/shims/shared/index.ts +14 -27
- package/shims/shared/module-context.ts +5 -5
- package/shims/shared/test-context.ts +27 -84
- package/shims/types.ts +1 -1
package/shims/shared/assert.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import '../../vendor/qunit.js';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import type { AssertionErrorConstructor, InspectFn, ModuleState, PushResultInfo, QUnitObject, TestState } from '../types.ts';
|
|
3
|
+
import { objectValues, objectValuesSubset, validateException, validateExpectedExceptionArgs } from './index.ts';
|
|
4
|
+
|
|
5
|
+
// Sentinel used by rejects() to distinguish "promise resolved" from "caught undefined".
|
|
6
|
+
const PENDING = Symbol('pending');
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* The assertion object passed to every test callback and lifecycle hook.
|
|
@@ -34,11 +37,11 @@ export default class Assert {
|
|
|
34
37
|
|
|
35
38
|
/** @internal */
|
|
36
39
|
constructor(module: ModuleState | null, test?: TestState) {
|
|
37
|
-
this.test = test || (module as ModuleState).
|
|
40
|
+
this.test = test || (module as ModuleState).testContext;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/** @internal */
|
|
41
|
-
_incrementAssertionCount() {
|
|
44
|
+
private _incrementAssertionCount() {
|
|
42
45
|
this.test.totalExecutedAssertions++;
|
|
43
46
|
}
|
|
44
47
|
|
|
@@ -80,22 +83,16 @@ export default class Assert {
|
|
|
80
83
|
* ```
|
|
81
84
|
*/
|
|
82
85
|
step(message: string): void {
|
|
83
|
-
let assertionMessage = message;
|
|
84
|
-
let result = !!message;
|
|
85
|
-
|
|
86
86
|
this.test.steps.push(message);
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
const result = typeof message === 'string' && message.length > 0;
|
|
89
|
+
const assertionMessage = result
|
|
90
|
+
? message
|
|
91
|
+
: (message === undefined || message === '')
|
|
92
|
+
? 'You must provide a message to assert.step'
|
|
93
|
+
: 'You must provide a string value to assert.step';
|
|
94
94
|
|
|
95
|
-
this.pushResult({
|
|
96
|
-
result,
|
|
97
|
-
message: assertionMessage
|
|
98
|
-
});
|
|
95
|
+
this.pushResult({ result, message: assertionMessage });
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
/**
|
|
@@ -159,12 +156,9 @@ export default class Assert {
|
|
|
159
156
|
* ```
|
|
160
157
|
*/
|
|
161
158
|
async(): () => void {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
this.test.asyncOps.push(done);
|
|
166
|
-
|
|
167
|
-
return () => { resolveFn(); };
|
|
159
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
160
|
+
this.test.asyncOps.push(promise);
|
|
161
|
+
return resolve;
|
|
168
162
|
}
|
|
169
163
|
|
|
170
164
|
/** @internal Used by the test runner to wait for all async operations to complete. */
|
|
@@ -320,7 +314,7 @@ export default class Assert {
|
|
|
320
314
|
throw new Assert.AssertionError({
|
|
321
315
|
actual,
|
|
322
316
|
expected,
|
|
323
|
-
message: message || `
|
|
317
|
+
message: message || `Values are not equal:${formatDiff(actual, expected)}`,
|
|
324
318
|
operator: '==',
|
|
325
319
|
stackStartFn: this.equal,
|
|
326
320
|
});
|
|
@@ -346,7 +340,7 @@ export default class Assert {
|
|
|
346
340
|
actual,
|
|
347
341
|
expected,
|
|
348
342
|
operator: '!=',
|
|
349
|
-
message: message || `
|
|
343
|
+
message: message || `Values are unexpectedly equal:${formatUnexpectedMatch(actual)}`,
|
|
350
344
|
stackStartFn: this.notEqual,
|
|
351
345
|
});
|
|
352
346
|
}
|
|
@@ -376,7 +370,7 @@ export default class Assert {
|
|
|
376
370
|
throw new Assert.AssertionError({
|
|
377
371
|
actual: targetActual,
|
|
378
372
|
expected: targetExpected,
|
|
379
|
-
message: message || `
|
|
373
|
+
message: message || `Own properties are not equal:${formatDiff(targetActual, targetExpected)}`,
|
|
380
374
|
stackStartFn: this.propEqual,
|
|
381
375
|
});
|
|
382
376
|
}
|
|
@@ -403,7 +397,7 @@ export default class Assert {
|
|
|
403
397
|
throw new Assert.AssertionError({
|
|
404
398
|
actual: targetActual,
|
|
405
399
|
expected: targetExpected,
|
|
406
|
-
message: message || `
|
|
400
|
+
message: message || `Own properties are unexpectedly equal:${formatUnexpectedMatch(targetActual)}`,
|
|
407
401
|
stackStartFn: this.notPropEqual,
|
|
408
402
|
});
|
|
409
403
|
}
|
|
@@ -430,7 +424,7 @@ export default class Assert {
|
|
|
430
424
|
throw new Assert.AssertionError({
|
|
431
425
|
actual: targetActual,
|
|
432
426
|
expected: targetExpected,
|
|
433
|
-
message: message || `
|
|
427
|
+
message: message || `Object does not contain expected properties:${formatDiff(targetActual, targetExpected)}`,
|
|
434
428
|
stackStartFn: this.propContains,
|
|
435
429
|
});
|
|
436
430
|
}
|
|
@@ -457,7 +451,7 @@ export default class Assert {
|
|
|
457
451
|
throw new Assert.AssertionError({
|
|
458
452
|
actual: targetActual,
|
|
459
453
|
expected: targetExpected,
|
|
460
|
-
message: message || `
|
|
454
|
+
message: message || `Object unexpectedly contains all expected properties:${formatUnexpectedMatch(targetActual)}`,
|
|
461
455
|
stackStartFn: this.notPropContains,
|
|
462
456
|
});
|
|
463
457
|
}
|
|
@@ -482,7 +476,7 @@ export default class Assert {
|
|
|
482
476
|
throw new Assert.AssertionError({
|
|
483
477
|
actual,
|
|
484
478
|
expected,
|
|
485
|
-
message: message || `
|
|
479
|
+
message: message || `Values are not deeply equal:${formatDiff(actual, expected)}`,
|
|
486
480
|
operator: 'deepEqual',
|
|
487
481
|
stackStartFn: this.deepEqual,
|
|
488
482
|
});
|
|
@@ -507,7 +501,7 @@ export default class Assert {
|
|
|
507
501
|
throw new Assert.AssertionError({
|
|
508
502
|
actual,
|
|
509
503
|
expected,
|
|
510
|
-
message: message || `
|
|
504
|
+
message: message || `Values are unexpectedly deeply equal:${formatUnexpectedMatch(actual)}`,
|
|
511
505
|
operator: 'notDeepEqual',
|
|
512
506
|
stackStartFn: this.notDeepEqual,
|
|
513
507
|
});
|
|
@@ -532,7 +526,7 @@ export default class Assert {
|
|
|
532
526
|
throw new Assert.AssertionError({
|
|
533
527
|
actual,
|
|
534
528
|
expected,
|
|
535
|
-
message: message || `
|
|
529
|
+
message: message || `Values are not strictly equal:${formatDiff(actual, expected)}`,
|
|
536
530
|
operator: 'strictEqual',
|
|
537
531
|
stackStartFn: this.strictEqual,
|
|
538
532
|
});
|
|
@@ -557,7 +551,7 @@ export default class Assert {
|
|
|
557
551
|
throw new Assert.AssertionError({
|
|
558
552
|
actual,
|
|
559
553
|
expected,
|
|
560
|
-
message: message || `
|
|
554
|
+
message: message || `Values are unexpectedly strictly equal:${formatUnexpectedMatch(actual)}`,
|
|
561
555
|
operator: 'notStrictEqual',
|
|
562
556
|
stackStartFn: this.notStrictEqual,
|
|
563
557
|
});
|
|
@@ -580,7 +574,7 @@ export default class Assert {
|
|
|
580
574
|
* ```
|
|
581
575
|
*/
|
|
582
576
|
throws(blockFn: unknown, expectedInput?: unknown, assertionMessage?: string): void {
|
|
583
|
-
this
|
|
577
|
+
this._incrementAssertionCount();
|
|
584
578
|
const [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'throws');
|
|
585
579
|
if (typeof blockFn !== 'function') {
|
|
586
580
|
throw new Assert.AssertionError({
|
|
@@ -591,15 +585,18 @@ export default class Assert {
|
|
|
591
585
|
});
|
|
592
586
|
}
|
|
593
587
|
|
|
588
|
+
let returnValue: unknown;
|
|
594
589
|
try {
|
|
595
|
-
blockFn();
|
|
590
|
+
returnValue = blockFn();
|
|
596
591
|
} catch (error) {
|
|
597
|
-
const [result,
|
|
592
|
+
const [result, , validatedMessage] = validateException(error, expected, message);
|
|
598
593
|
if (result === false) {
|
|
594
|
+
const expectedDesc = describeExpected(expected);
|
|
595
|
+
const receivedDesc = String(error);
|
|
599
596
|
throw new Assert.AssertionError({
|
|
600
|
-
actual:
|
|
601
|
-
expected:
|
|
602
|
-
message: validatedMessage,
|
|
597
|
+
actual: receivedDesc,
|
|
598
|
+
expected: expectedDesc,
|
|
599
|
+
message: validatedMessage || throwsFailMessage('throws', expectedDesc, receivedDesc),
|
|
603
600
|
stackStartFn: this.throws,
|
|
604
601
|
});
|
|
605
602
|
}
|
|
@@ -607,6 +604,15 @@ export default class Assert {
|
|
|
607
604
|
return;
|
|
608
605
|
}
|
|
609
606
|
|
|
607
|
+
if (returnValue !== null && typeof returnValue === 'object' && typeof (returnValue as PromiseLike<unknown>).then === 'function') {
|
|
608
|
+
throw new Assert.AssertionError({
|
|
609
|
+
actual: returnValue,
|
|
610
|
+
expected: expected,
|
|
611
|
+
message: 'Function passed to `assert.throws` returned a Promise — did you mean to use `assert.rejects`?',
|
|
612
|
+
stackStartFn: this.throws,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
610
616
|
throw new Assert.AssertionError({
|
|
611
617
|
actual: blockFn,
|
|
612
618
|
expected: expected,
|
|
@@ -643,16 +649,14 @@ export default class Assert {
|
|
|
643
649
|
});
|
|
644
650
|
}
|
|
645
651
|
|
|
646
|
-
let
|
|
647
|
-
let rejectionError;
|
|
652
|
+
let caught: unknown = PENDING;
|
|
648
653
|
try {
|
|
649
654
|
await promise;
|
|
650
655
|
} catch (error) {
|
|
651
|
-
|
|
652
|
-
rejectionError = error;
|
|
656
|
+
caught = error;
|
|
653
657
|
}
|
|
654
658
|
|
|
655
|
-
if (
|
|
659
|
+
if (caught === PENDING) {
|
|
656
660
|
throw new Assert.AssertionError({
|
|
657
661
|
actual: promise,
|
|
658
662
|
expected: expected,
|
|
@@ -661,28 +665,60 @@ export default class Assert {
|
|
|
661
665
|
});
|
|
662
666
|
}
|
|
663
667
|
|
|
664
|
-
const [result,
|
|
668
|
+
const [result, , validatedMessage] = validateException(caught, expected, message);
|
|
665
669
|
if (result === false) {
|
|
670
|
+
const expectedDesc = describeExpected(expected);
|
|
671
|
+
const receivedDesc = String(caught);
|
|
666
672
|
throw new Assert.AssertionError({
|
|
667
|
-
actual:
|
|
668
|
-
expected:
|
|
669
|
-
message: validatedMessage,
|
|
673
|
+
actual: receivedDesc,
|
|
674
|
+
expected: expectedDesc,
|
|
675
|
+
message: validatedMessage || throwsFailMessage('rejects', expectedDesc, receivedDesc),
|
|
670
676
|
stackStartFn: this.rejects,
|
|
671
677
|
});
|
|
672
678
|
}
|
|
673
679
|
}
|
|
674
|
-
}
|
|
680
|
+
}
|
|
675
681
|
|
|
676
|
-
function defaultMessage(actual: unknown, description: string, expected: unknown): string {
|
|
677
|
-
return `
|
|
678
682
|
|
|
679
|
-
|
|
683
|
+
const INSPECT_OPTIONS = { depth: 10, colors: true, compact: false as const };
|
|
680
684
|
|
|
681
|
-
|
|
685
|
+
const inspect = (value: unknown): string => Assert.inspect(value, INSPECT_OPTIONS);
|
|
682
686
|
|
|
683
|
-
|
|
687
|
+
function formatDiff(actual: unknown, expected: unknown): string {
|
|
688
|
+
const aStr = inspect(actual), bStr = inspect(expected);
|
|
689
|
+
const aLines = aStr.split('\n'), bLines = bStr.split('\n');
|
|
690
|
+
if (aLines.length === 1 && bLines.length === 1) return `\n\nActual: ${aStr}\nExpected: ${bStr}`;
|
|
691
|
+
const lines = lcsDiff(aLines, bLines).map(([t, line]) =>
|
|
692
|
+
t === '=' ? ` ${line}` : t === '-' ? `- ${line}` : `+ ${line}`
|
|
693
|
+
);
|
|
694
|
+
return `\n\n- Actual + Expected\n\n${lines.join('\n')}`;
|
|
684
695
|
}
|
|
685
696
|
|
|
686
|
-
|
|
687
|
-
|
|
697
|
+
const formatUnexpectedMatch = (actual: unknown): string => `\n\n${inspect(actual)}`;
|
|
698
|
+
|
|
699
|
+
const describeExpected = (expected: unknown): string =>
|
|
700
|
+
expected == null ? '(no pattern)' :
|
|
701
|
+
typeof expected === 'string' ? expected :
|
|
702
|
+
typeof expected === 'function' ? ((expected as { name?: string }).name || String(expected)) :
|
|
703
|
+
String(expected);
|
|
704
|
+
|
|
705
|
+
const throwsFailMessage = (method: string, pattern: string, received: string): string =>
|
|
706
|
+
`assert.${method}: thrown error did not match expected:\n\nPattern: ${pattern}\nReceived: ${received}`;
|
|
707
|
+
|
|
708
|
+
type DiffEntry = ['=' | '-' | '+', string];
|
|
709
|
+
|
|
710
|
+
function lcsDiff(a: string[], b: string[]): DiffEntry[] {
|
|
711
|
+
const m = a.length, n = b.length;
|
|
712
|
+
const dp: number[][] = Array.from({ length: m + 1 }, () => new Array<number>(n + 1).fill(0));
|
|
713
|
+
for (let i = 1; i <= m; i++)
|
|
714
|
+
for (let j = 1; j <= n; j++)
|
|
715
|
+
dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] + 1 : Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
716
|
+
const result: DiffEntry[] = [];
|
|
717
|
+
let i = m, j = n;
|
|
718
|
+
while (i > 0 || j > 0) {
|
|
719
|
+
if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) { result.push(['=', a[i - 1]]); i--; j--; }
|
|
720
|
+
else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) { result.push(['+', b[j - 1]]); j--; }
|
|
721
|
+
else { result.push(['-', a[i - 1]]); i--; }
|
|
722
|
+
}
|
|
723
|
+
return result.reverse();
|
|
688
724
|
}
|
package/shims/shared/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const hasOwn = Object.prototype.hasOwnProperty
|
|
1
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
2
2
|
|
|
3
3
|
export function objectType(obj: unknown): string {
|
|
4
4
|
if (typeof obj === 'undefined') {
|
|
@@ -32,12 +32,8 @@ export function objectType(obj: unknown): string {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function is(type: string, obj: unknown): boolean {
|
|
36
|
-
return objectType(obj) === type;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
export function objectValues(obj: unknown, allowArray = true): unknown {
|
|
40
|
-
const vals: Record<string, unknown> | unknown[] = allowArray &&
|
|
36
|
+
const vals: Record<string, unknown> | unknown[] = allowArray && objectType(obj) === 'array' ? [] : {};
|
|
41
37
|
|
|
42
38
|
for (const key in obj as object) {
|
|
43
39
|
if (hasOwn.call(obj, key)) {
|
|
@@ -50,13 +46,8 @@ export function objectValues(obj: unknown, allowArray = true): unknown {
|
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
/**
|
|
53
|
-
*
|
|
54
49
|
* Recursively clone an object into a plain object, taking only the
|
|
55
|
-
* subset of own enumerable properties that exist a given model.
|
|
56
|
-
*
|
|
57
|
-
* @param {any} obj
|
|
58
|
-
* @param {any} model
|
|
59
|
-
* @return {Object}
|
|
50
|
+
* subset of own enumerable properties that exist in a given model.
|
|
60
51
|
*/
|
|
61
52
|
export function objectValuesSubset(obj: unknown, model: unknown): unknown {
|
|
62
53
|
// Return primitive values unchanged to avoid false positives or confusing
|
|
@@ -95,14 +86,15 @@ export function validateExpectedExceptionArgs(
|
|
|
95
86
|
expected = undefined;
|
|
96
87
|
return [expected, message];
|
|
97
88
|
} else {
|
|
98
|
-
throw new Error(
|
|
89
|
+
throw new Error(
|
|
90
|
+
`assert.${assertionMethod} does not accept a string value for the expected argument.\n` +
|
|
91
|
+
'Use a non-string object value (e.g. RegExp or validator function) instead if necessary.',
|
|
92
|
+
);
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
|
-
const valid = !expected ||
|
|
102
|
-
// TODO: be more explicit here
|
|
103
|
-
expectedType === 'regexp' || expectedType === 'function' || expectedType === 'object';
|
|
95
|
+
const valid = !expected || expectedType === 'regexp' || expectedType === 'function' || expectedType === 'object';
|
|
104
96
|
if (!valid) {
|
|
105
|
-
throw new Error(
|
|
97
|
+
throw new Error(`Invalid expected value type (${expectedType}) provided to assert.${assertionMethod}.`);
|
|
106
98
|
}
|
|
107
99
|
return [expected, message];
|
|
108
100
|
}
|
|
@@ -170,15 +162,10 @@ function errorString(error: unknown): string {
|
|
|
170
162
|
// an object literal with name and message properties...
|
|
171
163
|
if (resultErrorString.slice(0, 7) === '[object') {
|
|
172
164
|
// Based on https://es5.github.io/#x15.11.4.4
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
? ': '.concat((error as { message: string }).message)
|
|
177
|
-
: '')
|
|
178
|
-
);
|
|
179
|
-
} else {
|
|
180
|
-
return resultErrorString;
|
|
165
|
+
const name = (error as { name?: string }).name || 'Error';
|
|
166
|
+
const msg = (error as { message?: string }).message;
|
|
167
|
+
return msg ? `${name}: ${msg}` : name;
|
|
181
168
|
}
|
|
182
|
-
}
|
|
183
169
|
|
|
184
|
-
|
|
170
|
+
return resultErrorString;
|
|
171
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type Assert from './assert.ts';
|
|
2
1
|
import type { HookFn } from '../types.ts';
|
|
2
|
+
import type Assert from './assert.ts';
|
|
3
3
|
import TestContext from './test-context.ts';
|
|
4
4
|
|
|
5
5
|
export default class ModuleContext {
|
|
@@ -7,7 +7,7 @@ export default class ModuleContext {
|
|
|
7
7
|
static currentModuleChain: ModuleContext[] = [];
|
|
8
8
|
|
|
9
9
|
static get lastModule() {
|
|
10
|
-
return this.currentModuleChain
|
|
10
|
+
return this.currentModuleChain.at(-1);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
name!: string;
|
|
@@ -15,7 +15,7 @@ export default class ModuleContext {
|
|
|
15
15
|
userContext!: Record<string, unknown>;
|
|
16
16
|
|
|
17
17
|
// Internal fallback assert for modules with no direct tests
|
|
18
|
-
|
|
18
|
+
testContext = new TestContext();
|
|
19
19
|
|
|
20
20
|
moduleChain: ModuleContext[] = [];
|
|
21
21
|
beforeEachHooks: HookFn<Assert>[] = [];
|
|
@@ -23,11 +23,11 @@ export default class ModuleContext {
|
|
|
23
23
|
tests: TestContext[] = [];
|
|
24
24
|
|
|
25
25
|
constructor(name: string) {
|
|
26
|
-
const parentModule = ModuleContext.currentModuleChain
|
|
26
|
+
const parentModule = ModuleContext.currentModuleChain.at(-1);
|
|
27
27
|
|
|
28
28
|
ModuleContext.currentModuleChain.push(this);
|
|
29
29
|
|
|
30
|
-
this.moduleChain = ModuleContext.currentModuleChain
|
|
30
|
+
this.moduleChain = [...ModuleContext.currentModuleChain];
|
|
31
31
|
this.name = parentModule ? `${parentModule.name} > ${name}` : name;
|
|
32
32
|
this.assert = new ModuleContext.Assert(this);
|
|
33
33
|
|
|
@@ -4,50 +4,29 @@ import type ModuleContext from './module-context.ts';
|
|
|
4
4
|
export default class TestContext {
|
|
5
5
|
static Assert: typeof Assert;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
get module() {
|
|
17
|
-
return this.#module;
|
|
18
|
-
}
|
|
19
|
-
set module(value) {
|
|
20
|
-
this.#module = value;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#asyncOps: Promise<void>[] = [];
|
|
24
|
-
get asyncOps() {
|
|
25
|
-
return this.#asyncOps;
|
|
26
|
-
}
|
|
27
|
-
set asyncOps(value) {
|
|
28
|
-
this.#asyncOps = value;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
#assert: Assert | undefined;
|
|
32
|
-
get assert() {
|
|
33
|
-
return this.#assert;
|
|
34
|
-
}
|
|
35
|
-
set assert(value) {
|
|
36
|
-
this.#assert = value;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
#timeout: number | undefined;
|
|
40
|
-
get timeout() {
|
|
41
|
-
return this.#timeout;
|
|
42
|
-
}
|
|
43
|
-
set timeout(value) {
|
|
44
|
-
this.#timeout = value;
|
|
45
|
-
}
|
|
7
|
+
name: string | undefined;
|
|
8
|
+
module: ModuleContext | undefined;
|
|
9
|
+
asyncOps: Promise<void>[] = [];
|
|
10
|
+
assert: Assert | undefined;
|
|
11
|
+
timeout: number | undefined;
|
|
12
|
+
steps: string[] = [];
|
|
13
|
+
expectedAssertionCount: number | undefined;
|
|
14
|
+
totalExecutedAssertions = 0;
|
|
15
|
+
userContext: Record<string, unknown> = {};
|
|
46
16
|
|
|
47
17
|
rejectTimeout: ((err: Error) => void) | undefined;
|
|
48
18
|
#timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
49
19
|
#timedOut = false;
|
|
50
20
|
|
|
21
|
+
constructor(name?: string, moduleContext?: ModuleContext) {
|
|
22
|
+
if (moduleContext) {
|
|
23
|
+
this.name = `${moduleContext.name} | ${name}`;
|
|
24
|
+
this.module = moduleContext;
|
|
25
|
+
this.module.tests.push(this);
|
|
26
|
+
this.assert = new TestContext.Assert(moduleContext, this);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
51
30
|
setTimeoutDuration(ms: number) {
|
|
52
31
|
if (this.#timeoutHandle !== undefined) {
|
|
53
32
|
clearTimeout(this.#timeoutHandle);
|
|
@@ -67,65 +46,29 @@ export default class TestContext {
|
|
|
67
46
|
}
|
|
68
47
|
}
|
|
69
48
|
|
|
70
|
-
#steps: string[] = [];
|
|
71
|
-
get steps() {
|
|
72
|
-
return this.#steps;
|
|
73
|
-
}
|
|
74
|
-
set steps(value) {
|
|
75
|
-
this.#steps = value;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
#expectedAssertionCount: number | undefined;
|
|
79
|
-
get expectedAssertionCount() {
|
|
80
|
-
return this.#expectedAssertionCount;
|
|
81
|
-
}
|
|
82
|
-
set expectedAssertionCount(value) {
|
|
83
|
-
this.#expectedAssertionCount = value;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
#totalExecutedAssertions = 0;
|
|
87
|
-
get totalExecutedAssertions() {
|
|
88
|
-
return this.#totalExecutedAssertions;
|
|
89
|
-
}
|
|
90
|
-
set totalExecutedAssertions(value) {
|
|
91
|
-
this.#totalExecutedAssertions = value;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
userContext: Record<string, unknown> = {};
|
|
95
|
-
|
|
96
|
-
constructor(name?: string, moduleContext?: ModuleContext) {
|
|
97
|
-
if (moduleContext) {
|
|
98
|
-
this.name = `${moduleContext.name} | ${name}`;
|
|
99
|
-
this.module = moduleContext;
|
|
100
|
-
this.module.tests.push(this);
|
|
101
|
-
this.assert = new TestContext.Assert(moduleContext, this);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
49
|
finish() {
|
|
106
50
|
if (this.#timedOut) return;
|
|
107
|
-
if (this.
|
|
108
|
-
this.assert!.pushResult({
|
|
109
|
-
result: false,
|
|
110
|
-
actual: this.totalExecutedAssertions,
|
|
111
|
-
expected: '> 0',
|
|
112
|
-
message: `Expected at least one assertion to be run for test: ${this.name}`,
|
|
113
|
-
});
|
|
114
|
-
} else if (this.steps.length > 0) {
|
|
51
|
+
if (this.steps.length > 0) {
|
|
115
52
|
this.assert!.pushResult({
|
|
116
53
|
result: false,
|
|
117
54
|
actual: this.steps,
|
|
118
55
|
expected: [],
|
|
119
56
|
message: `Expected assert.verifySteps() to be called before end of test after using assert.step(). Unverified steps: ${this.steps.join(', ')}`,
|
|
120
57
|
});
|
|
121
|
-
} else if (this.expectedAssertionCount && this.expectedAssertionCount !== this.totalExecutedAssertions) {
|
|
58
|
+
} else if (this.expectedAssertionCount !== undefined && this.expectedAssertionCount !== this.totalExecutedAssertions) {
|
|
122
59
|
this.assert!.pushResult({
|
|
123
60
|
result: false,
|
|
124
61
|
actual: this.totalExecutedAssertions,
|
|
125
62
|
expected: this.expectedAssertionCount,
|
|
126
63
|
message: `Expected ${this.expectedAssertionCount} assertions, but ${this.totalExecutedAssertions} were run for test: ${this.name}`,
|
|
127
64
|
});
|
|
65
|
+
} else if (this.expectedAssertionCount === undefined && this.totalExecutedAssertions === 0) {
|
|
66
|
+
this.assert!.pushResult({
|
|
67
|
+
result: false,
|
|
68
|
+
actual: this.totalExecutedAssertions,
|
|
69
|
+
expected: '> 0',
|
|
70
|
+
message: `Expected at least one assertion to be run for test: ${this.name}`,
|
|
71
|
+
});
|
|
128
72
|
}
|
|
129
73
|
}
|
|
130
74
|
}
|
|
131
|
-
|
package/shims/types.ts
CHANGED
|
@@ -44,7 +44,7 @@ export interface TestState {
|
|
|
44
44
|
|
|
45
45
|
/** Minimal module shape that Assert needs to resolve a fallback test context. */
|
|
46
46
|
export interface ModuleState {
|
|
47
|
-
|
|
47
|
+
testContext: TestState;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** A lifecycle hook callback that receives an {@linkcode Assert} instance and a meta object with the shared context. */
|