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