qunitx 1.0.3 → 1.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 +18 -5
- package/dist/browser/index.d.ts +31 -0
- package/{shims → dist}/browser/index.js +0 -3
- package/dist/node/index.d.ts +11 -0
- package/{shims → dist}/node/index.js +8 -10
- package/dist/node/module.d.ts +6 -0
- package/dist/node/module.js +49 -0
- package/dist/node/test.d.ts +8 -0
- package/dist/node/test.js +33 -0
- package/dist/shared/assert.d.ts +360 -0
- package/dist/shared/assert.js +647 -0
- package/dist/shared/index.d.ts +21 -0
- package/dist/shared/index.js +154 -0
- package/dist/shared/module-context.d.ts +17 -0
- package/dist/shared/module-context.js +28 -0
- package/dist/shared/test-context.d.ts +25 -0
- package/dist/shared/test-context.js +94 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.js +1 -0
- package/package.json +33 -22
- package/shims/deno/{index.js → index.ts} +10 -8
- package/shims/deno/module.ts +93 -0
- package/shims/deno/test.ts +77 -0
- package/shims/shared/{assert.js → assert.ts} +46 -40
- package/shims/shared/{index.js → index.ts} +45 -22
- package/shims/shared/module-context.ts +40 -0
- package/shims/shared/{test-context.js → test-context.ts} +17 -12
- package/shims/types.ts +66 -0
- package/vendor/qunit.d.ts +49 -0
- package/shims/deno/module.js +0 -98
- package/shims/deno/test.js +0 -65
- package/shims/node/module.js +0 -71
- package/shims/node/test.js +0 -36
- package/shims/shared/module-context.js +0 -29
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
import '../../vendor/qunit.js';
|
|
2
|
+
import { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The assertion object passed to every test callback and lifecycle hook.
|
|
5
|
+
*
|
|
6
|
+
* Every {@linkcode test} callback receives an instance of `Assert` as its first argument.
|
|
7
|
+
* All assertion methods throw an {@linkcode AssertionError} on failure, which the test
|
|
8
|
+
* runner catches and reports.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```js
|
|
12
|
+
* import { module, test } from "qunitx";
|
|
13
|
+
*
|
|
14
|
+
* module("Math", () => {
|
|
15
|
+
* test("addition", (assert) => {
|
|
16
|
+
* assert.equal(1 + 1, 2);
|
|
17
|
+
* assert.strictEqual(typeof 42, "number");
|
|
18
|
+
* });
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export default class Assert {
|
|
23
|
+
/** @internal Set by each runtime shim before tests run. */
|
|
24
|
+
static QUnit;
|
|
25
|
+
/** @internal Set by each runtime shim before tests run. */
|
|
26
|
+
static AssertionError;
|
|
27
|
+
/** @internal Set by each runtime shim before tests run. */
|
|
28
|
+
static inspect;
|
|
29
|
+
/** @internal Mutable test state written during the test run. */
|
|
30
|
+
test;
|
|
31
|
+
/** @internal */
|
|
32
|
+
constructor(module, test) {
|
|
33
|
+
this.test = test || module.context;
|
|
34
|
+
}
|
|
35
|
+
/** @internal */
|
|
36
|
+
_incrementAssertionCount() {
|
|
37
|
+
this.test.totalExecutedAssertions++;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Sets the number of milliseconds after which the current test will fail if not yet complete.
|
|
41
|
+
*
|
|
42
|
+
* @param {number} number - Timeout in milliseconds (positive integer).
|
|
43
|
+
* @example
|
|
44
|
+
* ```js
|
|
45
|
+
* test("slow async operation", async (assert) => {
|
|
46
|
+
* assert.timeout(500);
|
|
47
|
+
* await somethingAsync();
|
|
48
|
+
* assert.ok(true);
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
timeout(number) {
|
|
53
|
+
if (!Number.isInteger(number) || number < 0) {
|
|
54
|
+
throw new Error('assert.timeout() expects a positive integer.');
|
|
55
|
+
}
|
|
56
|
+
this.test.timeout = number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Records a named step. Use with {@linkcode Assert.prototype.verifySteps} to assert that
|
|
60
|
+
* a sequence of steps occurred in the right order.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} message - The step label to record.
|
|
63
|
+
* @example
|
|
64
|
+
* ```js
|
|
65
|
+
* test("event order", (assert) => {
|
|
66
|
+
* assert.expect(3);
|
|
67
|
+
* assert.step("step one");
|
|
68
|
+
* assert.step("step two");
|
|
69
|
+
* assert.verifySteps(["step one", "step two"]);
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
step(message) {
|
|
74
|
+
let assertionMessage = message;
|
|
75
|
+
let result = !!message;
|
|
76
|
+
this.test.steps.push(message);
|
|
77
|
+
if (typeof message === 'undefined' || message === '') {
|
|
78
|
+
assertionMessage = 'You must provide a message to assert.step';
|
|
79
|
+
}
|
|
80
|
+
else if (typeof message !== 'string') {
|
|
81
|
+
assertionMessage = 'You must provide a string value to assert.step';
|
|
82
|
+
result = false;
|
|
83
|
+
}
|
|
84
|
+
this.pushResult({
|
|
85
|
+
result,
|
|
86
|
+
message: assertionMessage
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Asserts that the steps recorded via {@linkcode Assert.prototype.step} match the given array,
|
|
91
|
+
* then clears the recorded steps.
|
|
92
|
+
*
|
|
93
|
+
* @param {string[]} steps - Expected array of step labels in order.
|
|
94
|
+
* @param {string} [message] - Optional failure message.
|
|
95
|
+
* @example
|
|
96
|
+
* ```js
|
|
97
|
+
* test("lifecycle order", (assert) => {
|
|
98
|
+
* assert.step("init");
|
|
99
|
+
* assert.step("run");
|
|
100
|
+
* assert.verifySteps(["init", "run"]);
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
verifySteps(steps, message = 'Verify steps failed!') {
|
|
105
|
+
this.deepEqual(this.test.steps, steps, message);
|
|
106
|
+
this.test.steps.length = 0;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Sets the number of assertions expected to run in the current test.
|
|
110
|
+
* The test fails if a different number of assertions actually ran.
|
|
111
|
+
*
|
|
112
|
+
* @param {number} number - Expected assertion count (non-negative integer).
|
|
113
|
+
* @example
|
|
114
|
+
* ```js
|
|
115
|
+
* test("exactly two assertions", (assert) => {
|
|
116
|
+
* assert.expect(2);
|
|
117
|
+
* assert.ok(true);
|
|
118
|
+
* assert.ok(true);
|
|
119
|
+
* });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
expect(number) {
|
|
123
|
+
if (!Number.isInteger(number) || number < 0) {
|
|
124
|
+
throw new Error('assert.expect() expects a positive integer.');
|
|
125
|
+
}
|
|
126
|
+
this.test.expectedAssertionCount = number;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Returns a `done` callback for callback-style async tests. The test will not
|
|
130
|
+
* finish until every `done` callback returned by `async()` has been called.
|
|
131
|
+
*
|
|
132
|
+
* For `async/await` tests prefer `async (assert) => { ... }` directly.
|
|
133
|
+
*
|
|
134
|
+
* @returns {function} A callback to invoke when the async work finishes.
|
|
135
|
+
* @example
|
|
136
|
+
* ```js
|
|
137
|
+
* test("async callback style", (assert) => {
|
|
138
|
+
* const done = assert.async();
|
|
139
|
+
* setTimeout(() => {
|
|
140
|
+
* assert.ok(true, "async callback ran");
|
|
141
|
+
* done();
|
|
142
|
+
* }, 10);
|
|
143
|
+
* });
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
async() {
|
|
147
|
+
let resolveFn;
|
|
148
|
+
const done = new Promise(resolve => { resolveFn = resolve; });
|
|
149
|
+
this.test.asyncOps.push(done);
|
|
150
|
+
return () => { resolveFn(); };
|
|
151
|
+
}
|
|
152
|
+
/** @internal Used by the test runner to wait for all async operations to complete. */
|
|
153
|
+
waitForAsyncOps() {
|
|
154
|
+
return Promise.all(this.test.asyncOps);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Pushes a custom assertion result. Fails the test if `resultInfo.result` is falsy.
|
|
158
|
+
* Throws an {@linkcode AssertionError} on failure.
|
|
159
|
+
*
|
|
160
|
+
* Useful for building custom assertion helpers.
|
|
161
|
+
*
|
|
162
|
+
* @param {{ result: boolean, actual?: unknown, expected?: unknown, message?: string }} resultInfo
|
|
163
|
+
* @example
|
|
164
|
+
* ```js
|
|
165
|
+
* test("custom assertion", (assert) => {
|
|
166
|
+
* assert.pushResult({
|
|
167
|
+
* result: 1 + 1 === 2,
|
|
168
|
+
* actual: 2,
|
|
169
|
+
* expected: 2,
|
|
170
|
+
* message: "custom math check",
|
|
171
|
+
* });
|
|
172
|
+
* });
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
pushResult(resultInfo = {}) {
|
|
176
|
+
this._incrementAssertionCount();
|
|
177
|
+
if (!resultInfo.result) {
|
|
178
|
+
throw new Assert.AssertionError({
|
|
179
|
+
actual: resultInfo.actual,
|
|
180
|
+
expected: resultInfo.expected,
|
|
181
|
+
message: resultInfo.message || 'Custom assertion failed!',
|
|
182
|
+
stackStartFn: this.pushResult,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Asserts that `state` is truthy.
|
|
189
|
+
*
|
|
190
|
+
* @param {unknown} state - The value to test.
|
|
191
|
+
* @param {string} [message] - Optional failure message.
|
|
192
|
+
* @example
|
|
193
|
+
* ```js
|
|
194
|
+
* assert.ok(true);
|
|
195
|
+
* assert.ok(1, "non-zero is truthy");
|
|
196
|
+
* assert.ok("hello");
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
ok(state, message) {
|
|
200
|
+
this._incrementAssertionCount();
|
|
201
|
+
if (!state) {
|
|
202
|
+
throw new Assert.AssertionError({
|
|
203
|
+
actual: state,
|
|
204
|
+
expected: true,
|
|
205
|
+
message: message || `Expected argument to be truthy, it was: ${inspect(state)}`,
|
|
206
|
+
stackStartFn: this.ok,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Asserts that `state` is falsy.
|
|
212
|
+
*
|
|
213
|
+
* @param {unknown} state - The value to test.
|
|
214
|
+
* @param {string} [message] - Optional failure message.
|
|
215
|
+
* @example
|
|
216
|
+
* ```js
|
|
217
|
+
* assert.notOk(false);
|
|
218
|
+
* assert.notOk(0, "zero is falsy");
|
|
219
|
+
* assert.notOk(null);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
notOk(state, message) {
|
|
223
|
+
this._incrementAssertionCount();
|
|
224
|
+
if (state) {
|
|
225
|
+
throw new Assert.AssertionError({
|
|
226
|
+
actual: state,
|
|
227
|
+
expected: false,
|
|
228
|
+
message: message || `Expected argument to be falsy, it was: ${inspect(state)}`,
|
|
229
|
+
stackStartFn: this.notOk,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Asserts that `state === true` (strict boolean true).
|
|
235
|
+
*
|
|
236
|
+
* @param {unknown} state - The value to test.
|
|
237
|
+
* @param {string} [message] - Optional failure message.
|
|
238
|
+
* @example
|
|
239
|
+
* ```js
|
|
240
|
+
* assert.true(1 === 1);
|
|
241
|
+
* assert.true(Array.isArray([]), "arrays are arrays");
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
true(state, message) {
|
|
245
|
+
this._incrementAssertionCount();
|
|
246
|
+
if (state !== true) {
|
|
247
|
+
throw new Assert.AssertionError({
|
|
248
|
+
actual: state,
|
|
249
|
+
expected: true,
|
|
250
|
+
message: message || `Expected argument to be true, it was: ${inspect(state)}`,
|
|
251
|
+
stackStartFn: this.true,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Asserts that `state === false` (strict boolean false).
|
|
257
|
+
*
|
|
258
|
+
* @param {unknown} state - The value to test.
|
|
259
|
+
* @param {string} [message] - Optional failure message.
|
|
260
|
+
* @example
|
|
261
|
+
* ```js
|
|
262
|
+
* assert.false(1 === 2);
|
|
263
|
+
* assert.false(Number.isNaN(42), "42 is not NaN");
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
false(state, message) {
|
|
267
|
+
this._incrementAssertionCount();
|
|
268
|
+
if (state !== false) {
|
|
269
|
+
throw new Assert.AssertionError({
|
|
270
|
+
actual: state,
|
|
271
|
+
expected: false,
|
|
272
|
+
message: message || `Expected argument to be false, it was: ${inspect(state)}`,
|
|
273
|
+
stackStartFn: this.false,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Asserts that `actual == expected` (loose equality, allows type coercion).
|
|
279
|
+
*
|
|
280
|
+
* Prefer {@linkcode Assert.prototype.strictEqual} for most comparisons. Use {@linkcode Assert.prototype.notEqual}
|
|
281
|
+
* for the inverse.
|
|
282
|
+
*
|
|
283
|
+
* @param {unknown} actual - The value produced by the code under test.
|
|
284
|
+
* @param {unknown} expected - The expected value.
|
|
285
|
+
* @param {string} [message] - Optional failure message.
|
|
286
|
+
* @example
|
|
287
|
+
* ```js
|
|
288
|
+
* assert.equal(1, 1);
|
|
289
|
+
* assert.equal("1", 1, "loose equality allows coercion");
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
equal(actual, expected, message) {
|
|
293
|
+
this._incrementAssertionCount();
|
|
294
|
+
if (actual != expected) {
|
|
295
|
+
throw new Assert.AssertionError({
|
|
296
|
+
actual,
|
|
297
|
+
expected,
|
|
298
|
+
message: message || `Expected: ${defaultMessage(actual, 'should equal to:', expected)}`,
|
|
299
|
+
operator: '==',
|
|
300
|
+
stackStartFn: this.equal,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Asserts that `actual != expected` (loose inequality). Inverse of {@linkcode Assert.prototype.equal}.
|
|
306
|
+
*
|
|
307
|
+
* @param {unknown} actual - The actual value.
|
|
308
|
+
* @param {unknown} expected - The value it should not loosely equal.
|
|
309
|
+
* @param {string} [message] - Optional failure message.
|
|
310
|
+
* @example
|
|
311
|
+
* ```js
|
|
312
|
+
* assert.notEqual(1, 2);
|
|
313
|
+
* assert.notEqual("hello", "world");
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
notEqual(actual, expected, message) {
|
|
317
|
+
this._incrementAssertionCount();
|
|
318
|
+
if (actual == expected) {
|
|
319
|
+
throw new Assert.AssertionError({
|
|
320
|
+
actual,
|
|
321
|
+
expected,
|
|
322
|
+
operator: '!=',
|
|
323
|
+
message: message || `Expected: ${defaultMessage(actual, 'should notEqual to:', expected)}`,
|
|
324
|
+
stackStartFn: this.notEqual,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Asserts that `actual` and `expected` have the same own enumerable properties
|
|
330
|
+
* and values. Prototype methods are ignored; only own properties are compared.
|
|
331
|
+
*
|
|
332
|
+
* @param {object} actual - The actual object.
|
|
333
|
+
* @param {object} expected - The expected object.
|
|
334
|
+
* @param {string} [message] - Optional failure message.
|
|
335
|
+
* @example
|
|
336
|
+
* ```js
|
|
337
|
+
* assert.propEqual({ a: 1, b: 2 }, { a: 1, b: 2 });
|
|
338
|
+
*
|
|
339
|
+
* // Ignores prototype methods — only own properties matter:
|
|
340
|
+
* function Point(x, y) { this.x = x; this.y = y; }
|
|
341
|
+
* assert.propEqual(new Point(1, 2), { x: 1, y: 2 });
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
propEqual(actual, expected, message) {
|
|
345
|
+
this._incrementAssertionCount();
|
|
346
|
+
const targetActual = objectValues(actual);
|
|
347
|
+
const targetExpected = objectValues(expected);
|
|
348
|
+
if (!Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
349
|
+
throw new Assert.AssertionError({
|
|
350
|
+
actual: targetActual,
|
|
351
|
+
expected: targetExpected,
|
|
352
|
+
message: message || `Expected properties to be propEqual: ${defaultMessage(targetActual, 'should propEqual to:', targetExpected)}`,
|
|
353
|
+
stackStartFn: this.propEqual,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Asserts that `actual` and `expected` do NOT have the same own enumerable
|
|
359
|
+
* properties and values. Inverse of {@linkcode Assert.prototype.propEqual}.
|
|
360
|
+
*
|
|
361
|
+
* @param {object} actual - The actual object.
|
|
362
|
+
* @param {object} expected - The value it should not propEqual.
|
|
363
|
+
* @param {string} [message] - Optional failure message.
|
|
364
|
+
* @example
|
|
365
|
+
* ```js
|
|
366
|
+
* assert.notPropEqual({ a: 1 }, { a: 2 });
|
|
367
|
+
* assert.notPropEqual({ a: 1, b: 2 }, { a: 1 }); // extra key makes them unequal
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
notPropEqual(actual, expected, message) {
|
|
371
|
+
this._incrementAssertionCount();
|
|
372
|
+
const targetActual = objectValues(actual);
|
|
373
|
+
const targetExpected = objectValues(expected);
|
|
374
|
+
if (Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
375
|
+
throw new Assert.AssertionError({
|
|
376
|
+
actual: targetActual,
|
|
377
|
+
expected: targetExpected,
|
|
378
|
+
message: message || `Expected properties to NOT be propEqual: ${defaultMessage(targetActual, 'should notPropEqual to:', targetExpected)}`,
|
|
379
|
+
stackStartFn: this.notPropEqual,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Asserts that `actual` contains all own enumerable properties from `expected`
|
|
385
|
+
* with matching values. Extra properties on `actual` are allowed and ignored.
|
|
386
|
+
*
|
|
387
|
+
* @param {object} actual - The actual object (may have extra keys).
|
|
388
|
+
* @param {object} expected - The subset of key/value pairs that must be present.
|
|
389
|
+
* @param {string} [message] - Optional failure message.
|
|
390
|
+
* @example
|
|
391
|
+
* ```js
|
|
392
|
+
* assert.propContains({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 });
|
|
393
|
+
* assert.propContains(user, { role: "admin" });
|
|
394
|
+
* ```
|
|
395
|
+
*/
|
|
396
|
+
propContains(actual, expected, message) {
|
|
397
|
+
this._incrementAssertionCount();
|
|
398
|
+
const targetActual = objectValuesSubset(actual, expected);
|
|
399
|
+
const targetExpected = objectValues(expected, false);
|
|
400
|
+
if (!Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
401
|
+
throw new Assert.AssertionError({
|
|
402
|
+
actual: targetActual,
|
|
403
|
+
expected: targetExpected,
|
|
404
|
+
message: message || `propContains assertion fail on: ${defaultMessage(targetActual, 'should propContains to:', targetExpected)}`,
|
|
405
|
+
stackStartFn: this.propContains,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Asserts that `actual` does NOT contain all own enumerable properties
|
|
411
|
+
* from `expected` with matching values. Inverse of {@linkcode Assert.prototype.propContains}.
|
|
412
|
+
*
|
|
413
|
+
* @param {object} actual - The actual object.
|
|
414
|
+
* @param {object} expected - The subset of properties that must NOT all match.
|
|
415
|
+
* @param {string} [message] - Optional failure message.
|
|
416
|
+
* @example
|
|
417
|
+
* ```js
|
|
418
|
+
* assert.notPropContains({ a: 1, b: 2 }, { a: 9 });
|
|
419
|
+
* assert.notPropContains(user, { role: "banned" });
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
notPropContains(actual, expected, message) {
|
|
423
|
+
this._incrementAssertionCount();
|
|
424
|
+
const targetActual = objectValuesSubset(actual, expected);
|
|
425
|
+
const targetExpected = objectValues(expected);
|
|
426
|
+
if (Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
427
|
+
throw new Assert.AssertionError({
|
|
428
|
+
actual: targetActual,
|
|
429
|
+
expected: targetExpected,
|
|
430
|
+
message: message || `notPropContains assertion fail on: ${defaultMessage(targetActual, 'should notPropContains of:', targetExpected)}`,
|
|
431
|
+
stackStartFn: this.notPropContains,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Asserts deep equality between `actual` and `expected` using recursive structural
|
|
437
|
+
* comparison. Handles nested objects, arrays, `Date`, `RegExp`, and more.
|
|
438
|
+
*
|
|
439
|
+
* @param {unknown} actual - The actual value.
|
|
440
|
+
* @param {unknown} expected - The expected value.
|
|
441
|
+
* @param {string} [message] - Optional failure message.
|
|
442
|
+
* @example
|
|
443
|
+
* ```js
|
|
444
|
+
* assert.deepEqual([1, { a: 2 }], [1, { a: 2 }]);
|
|
445
|
+
* assert.deepEqual(new Date("2024-01-01"), new Date("2024-01-01"));
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
deepEqual(actual, expected, message) {
|
|
449
|
+
this._incrementAssertionCount();
|
|
450
|
+
if (!Assert.QUnit.equiv(actual, expected)) {
|
|
451
|
+
throw new Assert.AssertionError({
|
|
452
|
+
actual,
|
|
453
|
+
expected,
|
|
454
|
+
message: message || `Expected values to be deepEqual: ${defaultMessage(actual, 'should deepEqual to:', expected)}`,
|
|
455
|
+
operator: 'deepEqual',
|
|
456
|
+
stackStartFn: this.deepEqual,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Asserts that `actual` and `expected` are NOT deeply equal. Inverse of {@linkcode Assert.prototype.deepEqual}.
|
|
462
|
+
*
|
|
463
|
+
* @param {unknown} actual - The actual value.
|
|
464
|
+
* @param {unknown} expected - The value it should not deepEqual.
|
|
465
|
+
* @param {string} [message] - Optional failure message.
|
|
466
|
+
* @example
|
|
467
|
+
* ```js
|
|
468
|
+
* assert.notDeepEqual([1, 2], [1, 3]);
|
|
469
|
+
* assert.notDeepEqual({ a: 1 }, { a: 2 });
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
notDeepEqual(actual, expected, message) {
|
|
473
|
+
this._incrementAssertionCount();
|
|
474
|
+
if (Assert.QUnit.equiv(actual, expected)) {
|
|
475
|
+
throw new Assert.AssertionError({
|
|
476
|
+
actual,
|
|
477
|
+
expected,
|
|
478
|
+
message: message || `Expected values to be NOT deepEqual: ${defaultMessage(actual, 'should notDeepEqual to:', expected)}`,
|
|
479
|
+
operator: 'notDeepEqual',
|
|
480
|
+
stackStartFn: this.notDeepEqual,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Asserts that `actual === expected` (strict equality, no type coercion).
|
|
486
|
+
*
|
|
487
|
+
* @param {unknown} actual - The actual value.
|
|
488
|
+
* @param {unknown} expected - The expected value.
|
|
489
|
+
* @param {string} [message] - Optional failure message.
|
|
490
|
+
* @example
|
|
491
|
+
* ```js
|
|
492
|
+
* assert.strictEqual(1 + 1, 2);
|
|
493
|
+
* assert.strictEqual(typeof "hello", "string");
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
strictEqual(actual, expected, message) {
|
|
497
|
+
this._incrementAssertionCount();
|
|
498
|
+
if (actual !== expected) {
|
|
499
|
+
throw new Assert.AssertionError({
|
|
500
|
+
actual,
|
|
501
|
+
expected,
|
|
502
|
+
message: message || `Expected: ${defaultMessage(actual, 'is strictEqual to:', expected)}`,
|
|
503
|
+
operator: 'strictEqual',
|
|
504
|
+
stackStartFn: this.strictEqual,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Asserts that `actual !== expected` (strict inequality). Inverse of {@linkcode Assert.prototype.strictEqual}.
|
|
510
|
+
*
|
|
511
|
+
* @param {unknown} actual - The actual value.
|
|
512
|
+
* @param {unknown} expected - The value it should not strictly equal.
|
|
513
|
+
* @param {string} [message] - Optional failure message.
|
|
514
|
+
* @example
|
|
515
|
+
* ```js
|
|
516
|
+
* assert.notStrictEqual(1, "1", "different types");
|
|
517
|
+
* assert.notStrictEqual({}, {}, "different object references");
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
520
|
+
notStrictEqual(actual, expected, message) {
|
|
521
|
+
this._incrementAssertionCount();
|
|
522
|
+
if (actual === expected) {
|
|
523
|
+
throw new Assert.AssertionError({
|
|
524
|
+
actual,
|
|
525
|
+
expected,
|
|
526
|
+
message: message || `Expected: ${defaultMessage(actual, 'is notStrictEqual to:', expected)}`,
|
|
527
|
+
operator: 'notStrictEqual',
|
|
528
|
+
stackStartFn: this.notStrictEqual,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Asserts that `blockFn` throws an exception. Optionally validates the thrown
|
|
534
|
+
* error against a string (message substring), RegExp (message pattern),
|
|
535
|
+
* or constructor (`instanceof` check). For async functions use {@linkcode Assert.prototype.rejects}.
|
|
536
|
+
*
|
|
537
|
+
* @param {function} blockFn - A synchronous function expected to throw.
|
|
538
|
+
* @param {string|RegExp|function} [expected] - Optional matcher for the thrown error.
|
|
539
|
+
* @param {string} [message] - Optional failure message.
|
|
540
|
+
* @example
|
|
541
|
+
* ```js
|
|
542
|
+
* assert.throws(() => { throw new Error("boom"); });
|
|
543
|
+
* assert.throws(() => JSON.parse("{bad}"), SyntaxError);
|
|
544
|
+
* assert.throws(() => { throw new Error("bad input"); }, /bad input/);
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
throws(blockFn, expectedInput, assertionMessage) {
|
|
548
|
+
this?._incrementAssertionCount();
|
|
549
|
+
const [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'throws');
|
|
550
|
+
if (typeof blockFn !== 'function') {
|
|
551
|
+
throw new Assert.AssertionError({
|
|
552
|
+
actual: blockFn,
|
|
553
|
+
expected: Function,
|
|
554
|
+
message: 'The value provided to `assert.throws` was not a function.',
|
|
555
|
+
stackStartFn: this.throws,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
try {
|
|
559
|
+
blockFn();
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
const [result, validatedExpected, validatedMessage] = validateException(error, expected, message);
|
|
563
|
+
if (result === false) {
|
|
564
|
+
throw new Assert.AssertionError({
|
|
565
|
+
actual: result,
|
|
566
|
+
expected: validatedExpected,
|
|
567
|
+
message: validatedMessage,
|
|
568
|
+
stackStartFn: this.throws,
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
throw new Assert.AssertionError({
|
|
574
|
+
actual: blockFn,
|
|
575
|
+
expected: expected,
|
|
576
|
+
message: 'Function passed to `assert.throws` did not throw an exception!',
|
|
577
|
+
stackStartFn: this.throws,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Asserts that a promise rejects. Optionally validates the rejection reason
|
|
582
|
+
* against a string (message substring), RegExp (message pattern),
|
|
583
|
+
* or constructor (`instanceof` check). For synchronous throws use {@linkcode Assert.prototype.throws}.
|
|
584
|
+
*
|
|
585
|
+
* @param {Promise<unknown>} promise - A promise expected to reject.
|
|
586
|
+
* @param {string|RegExp|function} [expected] - Optional matcher for the rejection reason.
|
|
587
|
+
* @param {string} [message] - Optional failure message.
|
|
588
|
+
* @example
|
|
589
|
+
* ```js
|
|
590
|
+
* await assert.rejects(Promise.reject(new Error("oops")));
|
|
591
|
+
* await assert.rejects(fetch("/bad-url"), TypeError);
|
|
592
|
+
* await assert.rejects(Promise.reject(new Error("timeout")), /timeout/);
|
|
593
|
+
* ```
|
|
594
|
+
*/
|
|
595
|
+
async rejects(promise, expectedInput, assertionMessage) {
|
|
596
|
+
this._incrementAssertionCount();
|
|
597
|
+
const [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
|
|
598
|
+
const then = promise && promise.then;
|
|
599
|
+
if (typeof then !== 'function') {
|
|
600
|
+
throw new Assert.AssertionError({
|
|
601
|
+
actual: promise,
|
|
602
|
+
expected: expected,
|
|
603
|
+
message: 'The value provided to `assert.rejects` was not a promise!',
|
|
604
|
+
stackStartFn: this.rejects,
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
let didReject = false;
|
|
608
|
+
let rejectionError;
|
|
609
|
+
try {
|
|
610
|
+
await promise;
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
didReject = true;
|
|
614
|
+
rejectionError = error;
|
|
615
|
+
}
|
|
616
|
+
if (!didReject) {
|
|
617
|
+
throw new Assert.AssertionError({
|
|
618
|
+
actual: promise,
|
|
619
|
+
expected: expected,
|
|
620
|
+
message: 'The promise returned by the `assert.rejects` callback did not reject!',
|
|
621
|
+
stackStartFn: this.rejects,
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
const [result, validatedExpected, validatedMessage] = validateException(rejectionError, expected, message);
|
|
625
|
+
if (result === false) {
|
|
626
|
+
throw new Assert.AssertionError({
|
|
627
|
+
actual: result,
|
|
628
|
+
expected: validatedExpected,
|
|
629
|
+
message: validatedMessage,
|
|
630
|
+
stackStartFn: this.rejects,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
;
|
|
636
|
+
function defaultMessage(actual, description, expected) {
|
|
637
|
+
return `
|
|
638
|
+
|
|
639
|
+
${inspect(actual)}
|
|
640
|
+
|
|
641
|
+
${description}
|
|
642
|
+
|
|
643
|
+
${inspect(expected)}`;
|
|
644
|
+
}
|
|
645
|
+
function inspect(value) {
|
|
646
|
+
return Assert.inspect(value, { depth: 10, colors: true, compact: false });
|
|
647
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare function objectType(obj: unknown): string;
|
|
2
|
+
export declare function objectValues(obj: unknown, allowArray?: boolean): unknown;
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* Recursively clone an object into a plain object, taking only the
|
|
6
|
+
* subset of own enumerable properties that exist a given model.
|
|
7
|
+
*
|
|
8
|
+
* @param {any} obj
|
|
9
|
+
* @param {any} model
|
|
10
|
+
* @return {Object}
|
|
11
|
+
*/
|
|
12
|
+
export declare function objectValuesSubset(obj: unknown, model: unknown): unknown;
|
|
13
|
+
export declare function validateExpectedExceptionArgs(expected: unknown, message: string | undefined, assertionMethod: string): [unknown, string | undefined];
|
|
14
|
+
export declare function validateException(actual: unknown, expected: unknown, message: string | undefined): [boolean, unknown, string | undefined];
|
|
15
|
+
declare const _default: {
|
|
16
|
+
objectValues: typeof objectValues;
|
|
17
|
+
objectValuesSubset: typeof objectValuesSubset;
|
|
18
|
+
validateExpectedExceptionArgs: typeof validateExpectedExceptionArgs;
|
|
19
|
+
validateException: typeof validateException;
|
|
20
|
+
};
|
|
21
|
+
export default _default;
|