qunitx 1.2.5 → 1.2.7

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,62 +1,17 @@
1
1
  class TestContext {
2
2
  static Assert;
3
- #name;
4
- get name() {
5
- return this.#name;
6
- }
7
- set name(value) {
8
- this.#name = value;
9
- }
10
- #module;
11
- get module() {
12
- return this.#module;
13
- }
14
- set module(value) {
15
- this.#module = value;
16
- }
17
- #asyncOps = [];
18
- get asyncOps() {
19
- return this.#asyncOps;
20
- }
21
- set asyncOps(value) {
22
- this.#asyncOps = value;
23
- }
24
- #assert;
25
- get assert() {
26
- return this.#assert;
27
- }
28
- set assert(value) {
29
- this.#assert = value;
30
- }
31
- #timeout;
32
- get timeout() {
33
- return this.#timeout;
34
- }
35
- set timeout(value) {
36
- this.#timeout = value;
37
- }
38
- #steps = [];
39
- get steps() {
40
- return this.#steps;
41
- }
42
- set steps(value) {
43
- this.#steps = value;
44
- }
45
- #expectedAssertionCount;
46
- get expectedAssertionCount() {
47
- return this.#expectedAssertionCount;
48
- }
49
- set expectedAssertionCount(value) {
50
- this.#expectedAssertionCount = value;
51
- }
52
- #totalExecutedAssertions = 0;
53
- get totalExecutedAssertions() {
54
- return this.#totalExecutedAssertions;
55
- }
56
- set totalExecutedAssertions(value) {
57
- this.#totalExecutedAssertions = value;
58
- }
3
+ name;
4
+ module;
5
+ asyncOps = [];
6
+ assert;
7
+ timeout;
8
+ steps = [];
9
+ expectedAssertionCount;
10
+ totalExecutedAssertions = 0;
59
11
  userContext = {};
12
+ rejectTimeout;
13
+ #timeoutHandle;
14
+ #timedOut = false;
60
15
  constructor(name, moduleContext) {
61
16
  if (moduleContext) {
62
17
  this.name = `${moduleContext.name} | ${name}`;
@@ -65,28 +20,46 @@ class TestContext {
65
20
  this.assert = new TestContext.Assert(moduleContext, this);
66
21
  }
67
22
  }
23
+ setTimeoutDuration(ms) {
24
+ if (this.#timeoutHandle !== void 0) {
25
+ clearTimeout(this.#timeoutHandle);
26
+ this.#timeoutHandle = void 0;
27
+ }
28
+ if (!this.rejectTimeout) return;
29
+ this.#timeoutHandle = setTimeout(() => {
30
+ this.#timedOut = true;
31
+ this.rejectTimeout(new Error(`Test timed out after ${ms}ms`));
32
+ }, ms);
33
+ }
34
+ clearTimeoutHandle() {
35
+ if (this.#timeoutHandle !== void 0) {
36
+ clearTimeout(this.#timeoutHandle);
37
+ this.#timeoutHandle = void 0;
38
+ }
39
+ }
68
40
  finish() {
69
- if (this.totalExecutedAssertions === 0) {
70
- this.assert.pushResult({
71
- result: false,
72
- actual: this.totalExecutedAssertions,
73
- expected: "> 0",
74
- message: `Expected at least one assertion to be run for test: ${this.name}`
75
- });
76
- } else if (this.steps.length > 0) {
41
+ if (this.#timedOut) return;
42
+ if (this.steps.length > 0) {
77
43
  this.assert.pushResult({
78
44
  result: false,
79
45
  actual: this.steps,
80
46
  expected: [],
81
47
  message: `Expected assert.verifySteps() to be called before end of test after using assert.step(). Unverified steps: ${this.steps.join(", ")}`
82
48
  });
83
- } else if (this.expectedAssertionCount && this.expectedAssertionCount !== this.totalExecutedAssertions) {
49
+ } else if (this.expectedAssertionCount !== void 0 && this.expectedAssertionCount !== this.totalExecutedAssertions) {
84
50
  this.assert.pushResult({
85
51
  result: false,
86
52
  actual: this.totalExecutedAssertions,
87
53
  expected: this.expectedAssertionCount,
88
54
  message: `Expected ${this.expectedAssertionCount} assertions, but ${this.totalExecutedAssertions} were run for test: ${this.name}`
89
55
  });
56
+ } else if (this.expectedAssertionCount === void 0 && this.totalExecutedAssertions === 0) {
57
+ this.assert.pushResult({
58
+ result: false,
59
+ actual: this.totalExecutedAssertions,
60
+ expected: "> 0",
61
+ message: `Expected at least one assertion to be run for test: ${this.name}`
62
+ });
90
63
  }
91
64
  }
92
65
  }
package/dist/types.d.ts CHANGED
@@ -40,10 +40,11 @@ export interface TestState {
40
40
  module?: {
41
41
  name: string;
42
42
  };
43
+ setTimeoutDuration(ms: number): void;
43
44
  }
44
45
  /** Minimal module shape that Assert needs to resolve a fallback test context. */
45
46
  export interface ModuleState {
46
- context: TestState;
47
+ testContext: TestState;
47
48
  }
48
49
  /** A lifecycle hook callback that receives an {@linkcode Assert} instance and a meta object with the shared context. */
49
50
  export type HookFn<A = unknown> = (assert: A, meta: {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "qunitx",
3
3
  "type": "module",
4
- "version": "1.2.5",
5
- "description": "A universal test framework for testing any js file on node.js, browser or deno with QUnit API",
4
+ "version": "1.2.7",
5
+ "description": "Universal test library run the same test file in Node.js, Deno, and the browser with QUnit's battle-tested assertion API",
6
6
  "author": "Izel Nakri",
7
7
  "license": "MIT",
8
8
  "keywords": [
@@ -24,12 +24,12 @@
24
24
  *
25
25
  * @module
26
26
  */
27
- import { AssertionError as DenoAssertionError } from "jsr:@std/assert";
28
- import type { AssertionErrorOptions } from "../types.ts";
29
27
  import '../../vendor/qunit.js';
28
+ import { AssertionError as DenoAssertionError } from 'jsr:@std/assert';
30
29
  import Assert from '../shared/assert.ts';
31
30
  import ModuleContext from '../shared/module-context.ts';
32
31
  import TestContext from '../shared/test-context.ts';
32
+ import type { AssertionErrorOptions } from '../types.ts';
33
33
  import Module from './module.ts';
34
34
  import Test from './test.ts';
35
35
 
@@ -111,6 +111,25 @@ export { Assert };
111
111
  */
112
112
  export { default as module } from './module.ts';
113
113
 
114
+ /**
115
+ * Registers a skipped test. Equivalent to `test.skip`. The test body is never
116
+ * executed and the test is reported as ignored by Deno's runner.
117
+ *
118
+ * @param {string} testName - Name of the test to skip.
119
+ * @param {function} [_testContent] - Optional body (ignored — the test will not run).
120
+ * @example
121
+ * ```js
122
+ * import { module, skip } from "qunitx";
123
+ *
124
+ * module("Math", () => {
125
+ * skip("addition is not yet implemented", (assert) => {
126
+ * assert.equal(1 + 1, 2);
127
+ * });
128
+ * });
129
+ * ```
130
+ */
131
+ export const skip = Test.skip;
132
+
114
133
  /**
115
134
  * Defines an individual test. Wraps Deno's `it()` and handles the full QUnit
116
135
  * lifecycle: `beforeEach`/`afterEach` hooks, async assertion waiting, and step
@@ -142,25 +161,6 @@ export { default as module } from './module.ts';
142
161
  * ```
143
162
  */
144
163
  export { default as test } from './test.ts';
145
-
146
- /**
147
- * Registers a skipped test. Equivalent to `test.skip`. The test body is never
148
- * executed and the test is reported as ignored by Deno's runner.
149
- *
150
- * @param {string} testName - Name of the test to skip.
151
- * @param {function} [_testContent] - Optional body (ignored — the test will not run).
152
- * @example
153
- * ```js
154
- * import { module, skip } from "qunitx";
155
- *
156
- * module("Math", () => {
157
- * skip("addition is not yet implemented", (assert) => {
158
- * assert.equal(1 + 1, 2);
159
- * });
160
- * });
161
- * ```
162
- */
163
- export const skip = Test.skip;
164
164
  export const todo = Test.todo;
165
165
 
166
166
  /**
@@ -1,9 +1,7 @@
1
- import { describe, beforeAll, afterAll } from "jsr:@std/testing/bdd";
1
+ import { afterAll, beforeAll, describe } from 'jsr:@std/testing/bdd';
2
2
  import type Assert from '../shared/assert.ts';
3
- import type { HookFn, HooksObject } from '../types.ts';
4
3
  import ModuleContext from '../shared/module-context.ts';
5
- export type { Assert };
6
- export type { HookFn, HooksObject, PushResultInfo } from '../types.ts';
4
+ import type { HookFn, HooksObject } from '../types.ts';
7
5
 
8
6
  /**
9
7
  * Defines a test module (suite) for Deno's BDD test runner.
@@ -53,14 +51,14 @@ export default function module(
53
51
  return;
54
52
  }
55
53
 
56
- const targetModuleContent = (moduleContent ? moduleContent : runtimeOptions) as (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void;
54
+ const targetModuleContent = (moduleContent ?? runtimeOptions) as (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void;
57
55
  const moduleContext = new ModuleContext(moduleName);
58
56
 
59
57
  describe(moduleName, { ...targetRuntimeOptions }, function () {
60
58
  const beforeHooks: HookFn<Assert>[] = [];
61
59
  const afterHooks: HookFn<Assert>[] = [];
62
60
 
63
- beforeAll(async function () {
61
+ beforeAll(async () => {
64
62
  // before() assertions are attributed to the first direct test only (matching QUnit's model).
65
63
  // Tests inherit parent context via prototype chain, so no Object.assign needed.
66
64
  const firstTest = moduleContext.tests[0];
@@ -72,19 +70,18 @@ export default function module(
72
70
  });
73
71
 
74
72
  afterAll(async () => {
75
- for (const testContext of moduleContext.tests) {
76
- await testContext.assert!.waitForAsyncOps();
77
- }
73
+ const allAsyncOps = moduleContext.tests.flatMap((t) => t.asyncOps);
74
+ if (allAsyncOps.length > 0) await Promise.all(allAsyncOps);
78
75
 
79
- const lastTest = moduleContext.tests[moduleContext.tests.length - 1];
76
+ const lastTest = moduleContext.tests.at(-1);
80
77
  if (lastTest) {
81
- for (let j = afterHooks.length - 1; j >= 0; j--) {
82
- await afterHooks[j]!.call(lastTest.userContext, lastTest.assert!, { context: lastTest.userContext });
78
+ for (const hook of afterHooks.toReversed()) {
79
+ await hook.call(lastTest.userContext, lastTest.assert!, { context: lastTest.userContext });
83
80
  }
84
81
  }
85
82
 
86
- for (let i = 0, len = moduleContext.tests.length; i < len; i++) {
87
- moduleContext.tests[i].finish();
83
+ for (const testCtx of moduleContext.tests) {
84
+ testCtx.finish();
88
85
  }
89
86
  });
90
87
 
@@ -101,7 +98,7 @@ export default function module(
101
98
  after(afterFn) {
102
99
  afterHooks.push(afterFn);
103
100
  }
104
- }, { moduleName, options: runtimeOptions, context: moduleContext.userContext });
101
+ }, { moduleName, options: targetRuntimeOptions, context: moduleContext.userContext });
105
102
 
106
103
  ModuleContext.currentModuleChain.pop();
107
104
  });
@@ -127,3 +124,28 @@ export default function module(
127
124
  module.skip = function skipModule(moduleName: string, _moduleContent?: unknown): void {
128
125
  describe(moduleName, { ignore: true }, function () {});
129
126
  };
127
+
128
+ /**
129
+ * Marks all tests inside a module as todo. Equivalent to `QUnit.module.todo`.
130
+ * The module is registered as ignored by Deno's runner; no test bodies run.
131
+ * Deno has no native "todo module" concept, so this maps to ignore.
132
+ *
133
+ * @param {string} moduleName - Name of the module to mark as todo.
134
+ * @param {function} [_moduleContent] - Optional body (ignored — no tests run).
135
+ * @example
136
+ * ```js ignore
137
+ * import { module, test } from "qunitx";
138
+ *
139
+ * module.todo("Math — not yet implemented", () => {
140
+ * test("addition", (assert) => {
141
+ * assert.equal(1 + 1, 2);
142
+ * });
143
+ * });
144
+ * ```
145
+ */
146
+ module.todo = function todoModule(moduleName: string, _moduleContent?: unknown): void {
147
+ describe(moduleName, { ignore: true }, function () {});
148
+ };
149
+
150
+ export type { Assert };
151
+ export type { HookFn, HooksObject, PushResultInfo } from '../types.ts';
@@ -1,9 +1,7 @@
1
- import { it } from "jsr:@std/testing/bdd";
1
+ import { it } from 'jsr:@std/testing/bdd';
2
2
  import type Assert from '../shared/assert.ts';
3
- import TestContext from '../shared/test-context.ts';
4
3
  import ModuleContext from '../shared/module-context.ts';
5
- export type { Assert };
6
- export type { PushResultInfo } from '../types.ts';
4
+ import TestContext from '../shared/test-context.ts';
7
5
 
8
6
  /**
9
7
  * Defines an individual test within a module for Deno's BDD test runner.
@@ -59,7 +57,7 @@ export default function test(
59
57
  return;
60
58
  }
61
59
 
62
- const targetTestContent = (testContent ? testContent : runtimeOptions) as (assert: Assert, meta: { testName: string; options: unknown }) => void | Promise<void>;
60
+ const targetTestContent = (testContent ?? runtimeOptions) as (assert: Assert, meta: { testName: string; options: unknown }) => void | Promise<void>;
63
61
  const context = new TestContext(testName, moduleContext);
64
62
 
65
63
  // Each test gets a fresh plain object inheriting from the module's user context.
@@ -68,27 +66,39 @@ export default function test(
68
66
  const userContext = Object.create(moduleContext.userContext);
69
67
  context.userContext = userContext;
70
68
 
71
- it(testName, { ...targetRuntimeOptions }, async function () {
69
+ it(testName, { ...targetRuntimeOptions }, function () {
70
+ const { promise, resolve, reject } = Promise.withResolvers<void>();
71
+ context.rejectTimeout = reject;
72
+
72
73
  const hookMeta = { context: userContext };
73
74
 
74
- for (const module of context.module!.moduleChain) {
75
- for (const hook of module.beforeEachHooks) {
76
- await hook.call(userContext, context.assert!, hookMeta);
77
- }
78
- }
75
+ (async () => {
76
+ try {
77
+ for (const mod of context.module!.moduleChain) {
78
+ for (const hook of mod.beforeEachHooks) {
79
+ await hook.call(userContext, context.assert!, hookMeta);
80
+ }
81
+ }
82
+
83
+ await targetTestContent.call(userContext, context.assert!, { testName, options: targetRuntimeOptions, context: userContext });
79
84
 
80
- const result = await targetTestContent.call(userContext, context.assert!, { testName, options: runtimeOptions, context: userContext });
85
+ await context.assert!.waitForAsyncOps();
81
86
 
82
- await context.assert!.waitForAsyncOps();
87
+ for (const mod of context.module!.moduleChain.toReversed()) {
88
+ for (const hook of mod.afterEachHooks.toReversed()) {
89
+ await hook.call(userContext, context.assert!, hookMeta);
90
+ }
91
+ }
83
92
 
84
- for (let i = context.module!.moduleChain.length - 1; i >= 0; i--) {
85
- const module = context.module!.moduleChain[i];
86
- for (let j = module.afterEachHooks.length - 1; j >= 0; j--) {
87
- await module.afterEachHooks[j]!.call(userContext, context.assert!, hookMeta);
93
+ resolve();
94
+ } catch (err) {
95
+ reject(err);
96
+ } finally {
97
+ context.clearTimeoutHandle();
88
98
  }
89
- }
99
+ })();
90
100
 
91
- return result;
101
+ return promise;
92
102
  });
93
103
  }
94
104
 
@@ -134,3 +144,6 @@ test.skip = function skipTest(testName: string, _testContent?: unknown): void {
134
144
  test.todo = function todoTest(testName: string, _testContent?: unknown): void {
135
145
  it(testName, { ignore: true }, async function () {});
136
146
  };
147
+
148
+ export type { Assert };
149
+ export type { PushResultInfo } from '../types.ts';
@@ -1,6 +1,9 @@
1
1
  import '../../vendor/qunit.js';
2
- import { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException } from './index.ts';
3
- import type { QUnitObject, AssertionErrorConstructor, InspectFn, TestState, ModuleState, PushResultInfo } from '../types.ts';
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).context;
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
 
@@ -61,6 +64,7 @@ export default class Assert {
61
64
  }
62
65
 
63
66
  this.test.timeout = number;
67
+ this.test.setTimeoutDuration(number);
64
68
  }
65
69
 
66
70
  /**
@@ -79,22 +83,16 @@ export default class Assert {
79
83
  * ```
80
84
  */
81
85
  step(message: string): void {
82
- let assertionMessage = message;
83
- let result = !!message;
84
-
85
86
  this.test.steps.push(message);
86
87
 
87
- if (typeof message === 'undefined' || message === '') {
88
- assertionMessage = 'You must provide a message to assert.step';
89
- } else if (typeof message !== 'string') {
90
- assertionMessage = 'You must provide a string value to assert.step';
91
- result = false;
92
- }
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';
93
94
 
94
- this.pushResult({
95
- result,
96
- message: assertionMessage
97
- });
95
+ this.pushResult({ result, message: assertionMessage });
98
96
  }
99
97
 
100
98
  /**
@@ -158,12 +156,9 @@ export default class Assert {
158
156
  * ```
159
157
  */
160
158
  async(): () => void {
161
- let resolveFn!: () => void;
162
- const done = new Promise<void>(resolve => { resolveFn = resolve; });
163
-
164
- this.test.asyncOps.push(done);
165
-
166
- return () => { resolveFn(); };
159
+ const { promise, resolve } = Promise.withResolvers<void>();
160
+ this.test.asyncOps.push(promise);
161
+ return resolve;
167
162
  }
168
163
 
169
164
  /** @internal Used by the test runner to wait for all async operations to complete. */
@@ -579,7 +574,7 @@ export default class Assert {
579
574
  * ```
580
575
  */
581
576
  throws(blockFn: unknown, expectedInput?: unknown, assertionMessage?: string): void {
582
- this?._incrementAssertionCount();
577
+ this._incrementAssertionCount();
583
578
  const [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'throws');
584
579
  if (typeof blockFn !== 'function') {
585
580
  throw new Assert.AssertionError({
@@ -590,8 +585,9 @@ export default class Assert {
590
585
  });
591
586
  }
592
587
 
588
+ let returnValue: unknown;
593
589
  try {
594
- blockFn();
590
+ returnValue = blockFn();
595
591
  } catch (error) {
596
592
  const [result, validatedExpected, validatedMessage] = validateException(error, expected, message);
597
593
  if (result === false) {
@@ -606,6 +602,15 @@ export default class Assert {
606
602
  return;
607
603
  }
608
604
 
605
+ if (returnValue !== null && typeof returnValue === 'object' && typeof (returnValue as PromiseLike<unknown>).then === 'function') {
606
+ throw new Assert.AssertionError({
607
+ actual: returnValue,
608
+ expected: expected,
609
+ message: 'Function passed to `assert.throws` returned a Promise — did you mean to use `assert.rejects`?',
610
+ stackStartFn: this.throws,
611
+ });
612
+ }
613
+
609
614
  throw new Assert.AssertionError({
610
615
  actual: blockFn,
611
616
  expected: expected,
@@ -642,16 +647,14 @@ export default class Assert {
642
647
  });
643
648
  }
644
649
 
645
- let didReject = false;
646
- let rejectionError;
650
+ let caught: unknown = PENDING;
647
651
  try {
648
652
  await promise;
649
653
  } catch (error) {
650
- didReject = true;
651
- rejectionError = error;
654
+ caught = error;
652
655
  }
653
656
 
654
- if (!didReject) {
657
+ if (caught === PENDING) {
655
658
  throw new Assert.AssertionError({
656
659
  actual: promise,
657
660
  expected: expected,
@@ -660,7 +663,7 @@ export default class Assert {
660
663
  });
661
664
  }
662
665
 
663
- const [result, validatedExpected, validatedMessage] = validateException(rejectionError, expected, message);
666
+ const [result, validatedExpected, validatedMessage] = validateException(caught, expected, message);
664
667
  if (result === false) {
665
668
  throw new Assert.AssertionError({
666
669
  actual: result,
@@ -670,7 +673,7 @@ export default class Assert {
670
673
  });
671
674
  }
672
675
  }
673
- };
676
+ }
674
677
 
675
678
  function defaultMessage(actual: unknown, description: string, expected: unknown): string {
676
679
  return `
@@ -682,6 +685,8 @@ ${description}
682
685
  ${inspect(expected)}`
683
686
  }
684
687
 
688
+ const INSPECT_OPTIONS = { depth: 10, colors: true, compact: false as const };
689
+
685
690
  function inspect(value: unknown): string {
686
- return Assert.inspect(value, { depth: 10, colors: true, compact: false });
691
+ return Assert.inspect(value, INSPECT_OPTIONS);
687
692
  }
@@ -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 && is('array', obj) ? [] : {};
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('assert.' + assertionMethod + ' does not accept a string value for the expected argument.\n' + 'Use a non-string object value (e.g. RegExp or validator function) ' + 'instead if necessary.');
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('Invalid expected value type (' + expectedType + ') ' + 'provided to assert.' + assertionMethod + '.');
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
- return (
174
- ((error as { name?: string }).name || 'Error') +
175
- ((error as { message?: string }).message
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
- export default { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException };
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[this.currentModuleChain.length - 1];
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
- context = new TestContext();
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[ModuleContext.currentModuleChain.length - 1];
26
+ const parentModule = ModuleContext.currentModuleChain.at(-1);
27
27
 
28
28
  ModuleContext.currentModuleChain.push(this);
29
29
 
30
- this.moduleChain = ModuleContext.currentModuleChain.slice(0);
30
+ this.moduleChain = [...ModuleContext.currentModuleChain];
31
31
  this.name = parentModule ? `${parentModule.name} > ${name}` : name;
32
32
  this.assert = new ModuleContext.Assert(this);
33
33