qunitx 1.2.6 → 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.
package/dist/types.d.ts CHANGED
@@ -44,7 +44,7 @@ export interface TestState {
44
44
  }
45
45
  /** Minimal module shape that Assert needs to resolve a fallback test context. */
46
46
  export interface ModuleState {
47
- context: TestState;
47
+ testContext: TestState;
48
48
  }
49
49
  /** A lifecycle hook callback that receives an {@linkcode Assert} instance and a meta object with the shared context. */
50
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.6",
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
  });
@@ -149,3 +146,6 @@ module.skip = function skipModule(moduleName: string, _moduleContent?: unknown):
149
146
  module.todo = function todoModule(moduleName: string, _moduleContent?: unknown): void {
150
147
  describe(moduleName, { ignore: true }, function () {});
151
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.
@@ -69,38 +67,38 @@ export default function test(
69
67
  context.userContext = userContext;
70
68
 
71
69
  it(testName, { ...targetRuntimeOptions }, function () {
72
- return new Promise((resolve, reject) => {
73
- context.rejectTimeout = reject;
70
+ const { promise, resolve, reject } = Promise.withResolvers<void>();
71
+ context.rejectTimeout = reject;
74
72
 
75
- (async () => {
76
- const hookMeta = { context: userContext };
73
+ const hookMeta = { context: userContext };
77
74
 
78
- try {
79
- for (const module of context.module!.moduleChain) {
80
- for (const hook of module.beforeEachHooks) {
81
- await hook.call(userContext, context.assert!, hookMeta);
82
- }
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);
83
80
  }
81
+ }
84
82
 
85
- const result = await targetTestContent.call(userContext, context.assert!, { testName, options: runtimeOptions, context: userContext });
83
+ await targetTestContent.call(userContext, context.assert!, { testName, options: targetRuntimeOptions, context: userContext });
86
84
 
87
- await context.assert!.waitForAsyncOps();
85
+ await context.assert!.waitForAsyncOps();
88
86
 
89
- for (let i = context.module!.moduleChain.length - 1; i >= 0; i--) {
90
- const module = context.module!.moduleChain[i];
91
- for (let j = module.afterEachHooks.length - 1; j >= 0; j--) {
92
- await module.afterEachHooks[j]!.call(userContext, context.assert!, hookMeta);
93
- }
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);
94
90
  }
95
-
96
- resolve(result);
97
- } catch (err) {
98
- reject(err);
99
- } finally {
100
- context.clearTimeoutHandle();
101
91
  }
102
- })();
103
- });
92
+
93
+ resolve();
94
+ } catch (err) {
95
+ reject(err);
96
+ } finally {
97
+ context.clearTimeoutHandle();
98
+ }
99
+ })();
100
+
101
+ return promise;
104
102
  });
105
103
  }
106
104
 
@@ -146,3 +144,6 @@ test.skip = function skipTest(testName: string, _testContent?: unknown): void {
146
144
  test.todo = function todoTest(testName: string, _testContent?: unknown): void {
147
145
  it(testName, { ignore: true }, async function () {});
148
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
 
@@ -80,22 +83,16 @@ export default class Assert {
80
83
  * ```
81
84
  */
82
85
  step(message: string): void {
83
- let assertionMessage = message;
84
- let result = !!message;
85
-
86
86
  this.test.steps.push(message);
87
87
 
88
- if (typeof message === 'undefined' || message === '') {
89
- assertionMessage = 'You must provide a message to assert.step';
90
- } else if (typeof message !== 'string') {
91
- assertionMessage = 'You must provide a string value to assert.step';
92
- result = false;
93
- }
88
+ const result = typeof message === 'string' && message.length > 0;
89
+ const assertionMessage = result
90
+ ? message
91
+ : (message === undefined || message === '')
92
+ ? 'You must provide a message to assert.step'
93
+ : 'You must provide a string value to assert.step';
94
94
 
95
- this.pushResult({
96
- result,
97
- message: assertionMessage
98
- });
95
+ this.pushResult({ result, message: assertionMessage });
99
96
  }
100
97
 
101
98
  /**
@@ -159,12 +156,9 @@ export default class Assert {
159
156
  * ```
160
157
  */
161
158
  async(): () => void {
162
- let resolveFn!: () => void;
163
- const done = new Promise<void>(resolve => { resolveFn = resolve; });
164
-
165
- this.test.asyncOps.push(done);
166
-
167
- return () => { resolveFn(); };
159
+ const { promise, resolve } = Promise.withResolvers<void>();
160
+ this.test.asyncOps.push(promise);
161
+ return resolve;
168
162
  }
169
163
 
170
164
  /** @internal Used by the test runner to wait for all async operations to complete. */
@@ -580,7 +574,7 @@ export default class Assert {
580
574
  * ```
581
575
  */
582
576
  throws(blockFn: unknown, expectedInput?: unknown, assertionMessage?: string): void {
583
- this?._incrementAssertionCount();
577
+ this._incrementAssertionCount();
584
578
  const [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'throws');
585
579
  if (typeof blockFn !== 'function') {
586
580
  throw new Assert.AssertionError({
@@ -591,8 +585,9 @@ export default class Assert {
591
585
  });
592
586
  }
593
587
 
588
+ let returnValue: unknown;
594
589
  try {
595
- blockFn();
590
+ returnValue = blockFn();
596
591
  } catch (error) {
597
592
  const [result, validatedExpected, validatedMessage] = validateException(error, expected, message);
598
593
  if (result === false) {
@@ -607,6 +602,15 @@ export default class Assert {
607
602
  return;
608
603
  }
609
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
+
610
614
  throw new Assert.AssertionError({
611
615
  actual: blockFn,
612
616
  expected: expected,
@@ -643,16 +647,14 @@ export default class Assert {
643
647
  });
644
648
  }
645
649
 
646
- let didReject = false;
647
- let rejectionError;
650
+ let caught: unknown = PENDING;
648
651
  try {
649
652
  await promise;
650
653
  } catch (error) {
651
- didReject = true;
652
- rejectionError = error;
654
+ caught = error;
653
655
  }
654
656
 
655
- if (!didReject) {
657
+ if (caught === PENDING) {
656
658
  throw new Assert.AssertionError({
657
659
  actual: promise,
658
660
  expected: expected,
@@ -661,7 +663,7 @@ export default class Assert {
661
663
  });
662
664
  }
663
665
 
664
- const [result, validatedExpected, validatedMessage] = validateException(rejectionError, expected, message);
666
+ const [result, validatedExpected, validatedMessage] = validateException(caught, expected, message);
665
667
  if (result === false) {
666
668
  throw new Assert.AssertionError({
667
669
  actual: result,
@@ -671,7 +673,7 @@ export default class Assert {
671
673
  });
672
674
  }
673
675
  }
674
- };
676
+ }
675
677
 
676
678
  function defaultMessage(actual: unknown, description: string, expected: unknown): string {
677
679
  return `
@@ -683,6 +685,8 @@ ${description}
683
685
  ${inspect(expected)}`
684
686
  }
685
687
 
688
+ const INSPECT_OPTIONS = { depth: 10, colors: true, compact: false as const };
689
+
686
690
  function inspect(value: unknown): string {
687
- return Assert.inspect(value, { depth: 10, colors: true, compact: false });
691
+ return Assert.inspect(value, INSPECT_OPTIONS);
688
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
 
@@ -4,50 +4,29 @@ import type ModuleContext from './module-context.ts';
4
4
  export default class TestContext {
5
5
  static Assert: typeof Assert;
6
6
 
7
- #name: string | undefined;
8
- get name() {
9
- return this.#name;
10
- }
11
- set name(value) {
12
- this.#name = value;
13
- }
14
-
15
- #module: ModuleContext | undefined;
16
- get module() {
17
- return this.#module;
18
- }
19
- set module(value) {
20
- this.#module = value;
21
- }
22
-
23
- #asyncOps: Promise<void>[] = [];
24
- get asyncOps() {
25
- return this.#asyncOps;
26
- }
27
- set asyncOps(value) {
28
- this.#asyncOps = value;
29
- }
30
-
31
- #assert: Assert | undefined;
32
- get assert() {
33
- return this.#assert;
34
- }
35
- set assert(value) {
36
- this.#assert = value;
37
- }
38
-
39
- #timeout: number | undefined;
40
- get timeout() {
41
- return this.#timeout;
42
- }
43
- set timeout(value) {
44
- this.#timeout = value;
45
- }
7
+ name: string | undefined;
8
+ module: ModuleContext | undefined;
9
+ asyncOps: Promise<void>[] = [];
10
+ assert: Assert | undefined;
11
+ timeout: number | undefined;
12
+ steps: string[] = [];
13
+ expectedAssertionCount: number | undefined;
14
+ totalExecutedAssertions = 0;
15
+ userContext: Record<string, unknown> = {};
46
16
 
47
17
  rejectTimeout: ((err: Error) => void) | undefined;
48
18
  #timeoutHandle: ReturnType<typeof setTimeout> | undefined;
49
19
  #timedOut = false;
50
20
 
21
+ constructor(name?: string, moduleContext?: ModuleContext) {
22
+ if (moduleContext) {
23
+ this.name = `${moduleContext.name} | ${name}`;
24
+ this.module = moduleContext;
25
+ this.module.tests.push(this);
26
+ this.assert = new TestContext.Assert(moduleContext, this);
27
+ }
28
+ }
29
+
51
30
  setTimeoutDuration(ms: number) {
52
31
  if (this.#timeoutHandle !== undefined) {
53
32
  clearTimeout(this.#timeoutHandle);
@@ -67,65 +46,29 @@ export default class TestContext {
67
46
  }
68
47
  }
69
48
 
70
- #steps: string[] = [];
71
- get steps() {
72
- return this.#steps;
73
- }
74
- set steps(value) {
75
- this.#steps = value;
76
- }
77
-
78
- #expectedAssertionCount: number | undefined;
79
- get expectedAssertionCount() {
80
- return this.#expectedAssertionCount;
81
- }
82
- set expectedAssertionCount(value) {
83
- this.#expectedAssertionCount = value;
84
- }
85
-
86
- #totalExecutedAssertions = 0;
87
- get totalExecutedAssertions() {
88
- return this.#totalExecutedAssertions;
89
- }
90
- set totalExecutedAssertions(value) {
91
- this.#totalExecutedAssertions = value;
92
- }
93
-
94
- userContext: Record<string, unknown> = {};
95
-
96
- constructor(name?: string, moduleContext?: ModuleContext) {
97
- if (moduleContext) {
98
- this.name = `${moduleContext.name} | ${name}`;
99
- this.module = moduleContext;
100
- this.module.tests.push(this);
101
- this.assert = new TestContext.Assert(moduleContext, this);
102
- }
103
- }
104
-
105
49
  finish() {
106
50
  if (this.#timedOut) return;
107
- if (this.totalExecutedAssertions === 0) {
108
- this.assert!.pushResult({
109
- result: false,
110
- actual: this.totalExecutedAssertions,
111
- expected: '> 0',
112
- message: `Expected at least one assertion to be run for test: ${this.name}`,
113
- });
114
- } else if (this.steps.length > 0) {
51
+ if (this.steps.length > 0) {
115
52
  this.assert!.pushResult({
116
53
  result: false,
117
54
  actual: this.steps,
118
55
  expected: [],
119
56
  message: `Expected assert.verifySteps() to be called before end of test after using assert.step(). Unverified steps: ${this.steps.join(', ')}`,
120
57
  });
121
- } else if (this.expectedAssertionCount && this.expectedAssertionCount !== this.totalExecutedAssertions) {
58
+ } else if (this.expectedAssertionCount !== undefined && this.expectedAssertionCount !== this.totalExecutedAssertions) {
122
59
  this.assert!.pushResult({
123
60
  result: false,
124
61
  actual: this.totalExecutedAssertions,
125
62
  expected: this.expectedAssertionCount,
126
63
  message: `Expected ${this.expectedAssertionCount} assertions, but ${this.totalExecutedAssertions} were run for test: ${this.name}`,
127
64
  });
65
+ } else if (this.expectedAssertionCount === undefined && this.totalExecutedAssertions === 0) {
66
+ this.assert!.pushResult({
67
+ result: false,
68
+ actual: this.totalExecutedAssertions,
69
+ expected: '> 0',
70
+ message: `Expected at least one assertion to be run for test: ${this.name}`,
71
+ });
128
72
  }
129
73
  }
130
74
  }
131
-
package/shims/types.ts CHANGED
@@ -44,7 +44,7 @@ export interface TestState {
44
44
 
45
45
  /** Minimal module shape that Assert needs to resolve a fallback test context. */
46
46
  export interface ModuleState {
47
- context: TestState;
47
+ testContext: TestState;
48
48
  }
49
49
 
50
50
  /** A lifecycle hook callback that receives an {@linkcode Assert} instance and a meta object with the shared context. */