qunitx 0.8.3 → 0.9.1

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/package.json CHANGED
@@ -1,14 +1,42 @@
1
1
  {
2
2
  "name": "qunitx",
3
3
  "type": "module",
4
- "version": "0.8.3",
4
+ "version": "0.9.1",
5
5
  "description": "A universal test framework for testing any js file on node.js, browser or deno with QUnit API",
6
6
  "author": "Izel Nakri",
7
7
  "license": "MIT",
8
8
  "keywords": [
9
9
  "test",
10
- "test framework",
11
10
  "testing",
11
+ "automated testing",
12
+ "test framework",
13
+ "ui testing",
14
+ "e2e",
15
+ "e2e testing",
16
+ "0 dependencies",
17
+ "zero dependencies",
18
+ "no dependencies",
19
+ "universal testing",
20
+ "universal test framework",
21
+ "universal test",
22
+ "universal",
23
+ "cross platform",
24
+ "cross platform testing",
25
+ "cross platform test framework",
26
+ "cross platform test",
27
+ "unit testing",
28
+ "unit test",
29
+ "unit",
30
+ "integration testing",
31
+ "integration test",
32
+ "integration",
33
+ "qa",
34
+ "quality assurance",
35
+ "tdd",
36
+ "bdd",
37
+ "assert",
38
+ "frontend",
39
+ "backend",
12
40
  "qunit",
13
41
  "node",
14
42
  "deno",
@@ -61,7 +89,7 @@
61
89
  "ts-node": ">=10.7.0"
62
90
  },
63
91
  "volta": {
64
- "node": "20.4.0"
92
+ "node": "20.5.0"
65
93
  },
66
94
  "prettier": {
67
95
  "printWidth": 100,
@@ -1,48 +1,27 @@
1
- import {
2
- afterEach,
3
- beforeEach,
4
- beforeAll,
5
- afterAll,
6
- describe,
7
- it,
8
- } from "https://deno.land/std@0.192.0/testing/bdd.ts";
9
- import assert from './assert.js';
10
-
11
- // TODO: TEST beforeEach, before, afterEach, after, currently not sure if they work!
12
- export const module = async function(moduleName, runtimeOptions, moduleContent) {
13
- let targetRuntimeOptions = moduleContent ? Object.assign(runtimeOptions, { name: moduleName }) : { name: moduleName };
14
- let targetModuleContent = moduleContent ? moduleName : runtimeOptions;
15
-
16
- return describe(assignDefaultValues(targetRuntimeOptions, { concurrency: true }), async function() {
17
- return await targetModuleContent({ before: beforeAll, after: afterAll, beforeEach, afterEach }, {
18
- moduleName,
19
- options: runtimeOptions
20
- });
21
- });
1
+ import { AssertionError as DenoAssertionError } from "https://deno.land/std@0.192.0/testing/asserts.ts";
2
+ import '../../vendor/qunit.js';
3
+ import Assert from '../shared/assert.js';
4
+ import ModuleContext from '../shared/module-context.js';
5
+ import TestContext from '../shared/test-context.js';
6
+ import Module from './module.js';
7
+ import Test from './test.js';
8
+
9
+ export class AssertionError extends DenoAssertionError {
10
+ constructor(object) {
11
+ super(object.message);
12
+ }
22
13
  }
23
14
 
24
- export const test = async function(testName, runtimeOptions, testContent) {
25
- let targetRuntimeOptions = testContent ? Object.assign(runtimeOptions, { name: testName }) : { name: testName };
26
- let targetTestContent = testContent ? testContent : runtimeOptions;
15
+ Assert.QUnit = QUnit;
16
+ Assert.AssertionError = AssertionError;
17
+ ModuleContext.Assert = Assert;
18
+ TestContext.Assert = Assert;
27
19
 
28
- return it(targetRuntimeOptions, async function() {
29
- let metadata = { testName, options: targetRuntimeOptions, expectedTestCount: undefined };
30
- return await targetTestContent(assert, metadata);
20
+ Object.freeze(Assert);
21
+ Object.freeze(ModuleContext);
22
+ Object.freeze(TestContext);
31
23
 
32
- if (expectedTestCount) {
33
-
34
- }
35
- });
36
- }
37
-
38
- function assignDefaultValues(options, defaultValues) {
39
- for (let key in defaultValues) {
40
- if (options[key] === undefined) {
41
- options[key] = defaultValues[key];
42
- }
43
- }
44
-
45
- return options;
46
- }
24
+ export const module = Module;
25
+ export const test = Test;
47
26
 
48
- export default { module, test, assert };
27
+ export default { AssertionError: Assert.AssertionError, module, test, config: {} };
@@ -0,0 +1,71 @@
1
+ import { describe, beforeAll, afterAll } from "https://deno.land/std@0.192.0/testing/bdd.ts";
2
+ import ModuleContext from '../shared/module-context.js';
3
+
4
+ // NOTE: node.js beforeEach & afterEach is buggy because the TestContext it has is NOT correct reference when called, it gets the last context
5
+ // NOTE: QUnit expect() logic is buggy in nested modules
6
+ // NOTE: after gets the last direct children test of the module, not last defined context of a module(last defined context is a module)
7
+
8
+ export default function module(moduleName, runtimeOptions, moduleContent) {
9
+ let targetRuntimeOptions = moduleContent ? runtimeOptions : {};
10
+ let targetModuleContent = moduleContent ? moduleContent : runtimeOptions;
11
+ let moduleContext = new ModuleContext(moduleName);
12
+
13
+ return describe(moduleName, { concurrency: true, ...targetRuntimeOptions }, async function () {
14
+ let beforeHooks = [];
15
+ let afterHooks = [];
16
+
17
+ beforeAll(async function () {
18
+ Object.assign(moduleContext.context, moduleContext.moduleChain.reduce((result, module) => {
19
+ return Object.assign(result, module.context, {
20
+ steps: result.steps.concat(module.context.steps),
21
+ expectedAssertionCount: module.context.expectedAssertionCount
22
+ ? module.context.expectedAssertionCount
23
+ : result.expectedAssertionCount
24
+ });
25
+ }, { steps: [], expectedAssertionCount: undefined }));
26
+
27
+ for (let hook of beforeHooks) {
28
+ await hook.call(moduleContext.context, moduleContext.assert);
29
+ }
30
+
31
+ for (let i = 0, len = moduleContext.tests.length; i < len; i++) {
32
+ Object.assign(moduleContext.tests[i], moduleContext.context, {
33
+ steps: moduleContext.context.steps,
34
+ totalExecutedAssertions: moduleContext.context.totalExecutedAssertions,
35
+ expectedAssertionCount: moduleContext.context.expectedAssertionCount,
36
+ });
37
+ }
38
+ });
39
+ afterAll(async () => {
40
+ for (const assert of moduleContext.tests.map(testContext => testContext.assert)) {
41
+ await assert.waitForAsyncOps();
42
+ }
43
+
44
+ let targetContext = moduleContext.tests[moduleContext.tests.length - 1];
45
+ for (let j = afterHooks.length - 1; j >= 0; j--) {
46
+ await afterHooks[j].call(targetContext, targetContext.assert);
47
+ }
48
+
49
+ for (let i = 0, len = moduleContext.tests.length; i < len; i++) {
50
+ moduleContext.tests[i].finish();
51
+ }
52
+ });
53
+
54
+ targetModuleContent.call(moduleContext.context, {
55
+ before(beforeFn) {
56
+ beforeHooks[beforeHooks.length] = beforeFn;
57
+ },
58
+ beforeEach(beforeEachFn) {
59
+ moduleContext.beforeEachHooks[moduleContext.beforeEachHooks.length] = beforeEachFn;
60
+ },
61
+ afterEach(afterEachFn) {
62
+ moduleContext.afterEachHooks[moduleContext.afterEachHooks.length] = afterEachFn;
63
+ },
64
+ after(afterFn) {
65
+ afterHooks[afterHooks.length] = afterFn;
66
+ }
67
+ }, { moduleName, options: runtimeOptions });
68
+
69
+ ModuleContext.currentModuleChain.pop();
70
+ });
71
+ }
@@ -0,0 +1,37 @@
1
+ import { it } from "https://deno.land/std@0.192.0/testing/bdd.ts";
2
+ import TestContext from '../shared/test-context.js';
3
+ import ModuleContext from '../shared/module-context.js';
4
+
5
+ export default function test(testName, runtimeOptions, testContent) {
6
+ let moduleContext = ModuleContext.lastModule;
7
+ if (!moduleContext) {
8
+ throw new Error(`Test '${testName}' called outside of module context.`);
9
+ }
10
+
11
+ let targetRuntimeOptions = testContent ? runtimeOptions : {};
12
+ let targetTestContent = testContent ? testContent : runtimeOptions;
13
+ let context = new TestContext(testName, moduleContext);
14
+
15
+ return it(testName, { concurrency: true, ...targetRuntimeOptions }, async function () {
16
+ let result;
17
+ for (let module of context.module.moduleChain) {
18
+ for (let hook of module.beforeEachHooks) {
19
+ await hook.call(context, context.assert);
20
+ }
21
+ }
22
+
23
+ result = await targetTestContent.call(context, context.assert, { testName, options: runtimeOptions });
24
+
25
+ await context.assert.waitForAsyncOps();
26
+
27
+ for (let i = context.module.moduleChain.length - 1; i >= 0; i--) {
28
+ let module = context.module.moduleChain[i];
29
+ for (let j = module.afterEachHooks.length - 1; j >= 0; j--) {
30
+ await module.afterEachHooks[j].call(context, context.assert);
31
+ }
32
+ }
33
+
34
+ return result;
35
+ });
36
+ }
37
+
@@ -1,75 +1,22 @@
1
- import { run, describe, it, before, after, beforeEach, afterEach } from 'node:test';
2
- import assert from './assert.js';
1
+ import { AssertionError } from 'node:assert';
2
+ import QUnit from '../../vendor/qunit.js';
3
+ import Assert from '../shared/assert.js';
4
+ import ModuleContext from '../shared/module-context.js';
5
+ import TestContext from '../shared/test-context.js';
6
+ import Module from './module.js';
7
+ import Test from './test.js';
3
8
 
4
- export const module = async function(moduleName, runtimeOptions, moduleContent) {
5
- let targetRuntimeOptions = moduleContent ? runtimeOptions : {};
6
- let targetModuleContent = moduleContent ? moduleContent : runtimeOptions;
9
+ Assert.QUnit = QUnit;
10
+ Assert.AssertionError = AssertionError;
7
11
 
8
- return describe(moduleName, assignDefaultValues(targetRuntimeOptions, { concurrency: true }), async function() {
9
- return await targetModuleContent({ before, after, beforeEach, afterEach }, {
10
- moduleName,
11
- options: runtimeOptions
12
- });
13
- });
14
- }
12
+ ModuleContext.Assert = Assert;
13
+ TestContext.Assert = Assert;
15
14
 
16
- export const test = async function(testName, runtimeOptions, testContent) {
17
- let targetRuntimeOptions = testContent ? runtimeOptions : {};
18
- let targetTestContent = testContent ? testContent : runtimeOptions;
15
+ Object.freeze(Assert);
16
+ Object.freeze(ModuleContext);
17
+ Object.freeze(TestContext);
19
18
 
20
- return it(testName, assignDefaultValues(targetRuntimeOptions, { concurrency: true }), async function() {
21
- return await targetTestContent(assert, { testName, options: runtimeOptions });
22
- });
23
- }
19
+ export const module = Module;
20
+ export const test = Test;
24
21
 
25
- function assignDefaultValues(options, defaultValues) {
26
- for (let key in defaultValues) {
27
- if (options[key] === undefined) {
28
- options[key] = defaultValues[key];
29
- }
30
- }
31
-
32
- return options;
33
- }
34
-
35
- export default { module, test, config: {} };
36
-
37
- // NOTE: later maybe expose these as well:
38
-
39
- // import QUnit from './vendor/qunit.js';
40
-
41
- // QUnit.config.autostart = false;
42
-
43
- // export const isLocal = QUnit.isLocal;
44
- // export const on = QUnit.on;
45
- // export const test = QUnit.test;
46
- // export const skip = QUnit.skip;
47
- // export const start = QUnit.start;
48
- // export const is = QUnit.is;
49
- // export const extend = QUnit.extend;
50
- // export const stack = QUnit.stack;
51
- // export const onUnhandledRejection = QUnit.onUnhandledRejection;
52
- // export const assert = QUnit.assert;
53
- // export const dump = QUnit.dump;
54
- // export const done = QUnit.done;
55
- // export const testStart = QUnit.testStart;
56
- // export const moduleStart = QUnit.moduleStart;
57
- // export const version = QUnit.version;
58
- // export const module = QUnit.module;
59
- // export const todo = QUnit.todo;
60
- // export const only = QUnit.only;
61
- // export const config = QUnit.config;
62
- // export const objectType = QUnit.objectType;
63
- // export const load = QUnit.load;
64
- // export const onError = QUnit.onError;
65
- // export const pushFailure = QUnit.pushFailure;
66
- // export const equiv = QUnit.equiv;
67
- // export const begin = QUnit.begin;
68
- // export const log = QUnit.log;
69
- // export const testDone = QUnit.testDone;
70
- // export const moduleDone = QUnit.moduleDone;
71
- // export const diff = QUnit.diff;
72
-
73
- // export default Object.assign(QUnit, {
74
- // QUnitxVersion: '0.0.1'
75
- // });
22
+ export default { AssertionError: Assert.AssertionError, module, test, config: {} };
@@ -0,0 +1,71 @@
1
+ import { describe, before as beforeAll, after as afterAll } from 'node:test';
2
+ import ModuleContext from '../shared/module-context.js';
3
+
4
+ // NOTE: node.js beforeEach & afterEach is buggy because the TestContext it has is NOT correct reference when called, it gets the last context
5
+ // NOTE: QUnit expect() logic is buggy in nested modules
6
+ // NOTE: after gets the last direct children test of the module, not last defined context of a module(last defined context is a module)
7
+
8
+ export default function module(moduleName, runtimeOptions, moduleContent) {
9
+ let targetRuntimeOptions = moduleContent ? runtimeOptions : {};
10
+ let targetModuleContent = moduleContent ? moduleContent : runtimeOptions;
11
+ let moduleContext = new ModuleContext(moduleName);
12
+
13
+ return describe(moduleName, { concurrency: true, ...targetRuntimeOptions }, async function () {
14
+ let beforeHooks = [];
15
+ let afterHooks = [];
16
+
17
+ beforeAll(async function () {
18
+ Object.assign(moduleContext.context, moduleContext.moduleChain.reduce((result, module) => {
19
+ return Object.assign(result, module.context, {
20
+ steps: result.steps.concat(module.context.steps),
21
+ expectedAssertionCount: module.context.expectedAssertionCount
22
+ ? module.context.expectedAssertionCount
23
+ : result.expectedAssertionCount
24
+ });
25
+ }, { steps: [], expectedAssertionCount: undefined }));
26
+
27
+ for (let hook of beforeHooks) {
28
+ await hook.call(moduleContext.context, moduleContext.assert);
29
+ }
30
+
31
+ for (let i = 0, len = moduleContext.tests.length; i < len; i++) {
32
+ Object.assign(moduleContext.tests[i], moduleContext.context, {
33
+ steps: moduleContext.context.steps,
34
+ totalExecutedAssertions: moduleContext.context.totalExecutedAssertions,
35
+ expectedAssertionCount: moduleContext.context.expectedAssertionCount,
36
+ });
37
+ }
38
+ });
39
+ afterAll(async () => {
40
+ for (const assert of moduleContext.tests.map(testContext => testContext.assert)) {
41
+ await assert.waitForAsyncOps();
42
+ }
43
+
44
+ let targetContext = moduleContext.tests[moduleContext.tests.length - 1];
45
+ for (let j = afterHooks.length - 1; j >= 0; j--) {
46
+ await afterHooks[j].call(targetContext, targetContext.assert);
47
+ }
48
+
49
+ for (let i = 0, len = moduleContext.tests.length; i < len; i++) {
50
+ moduleContext.tests[i].finish();
51
+ }
52
+ });
53
+
54
+ targetModuleContent.call(moduleContext.context, {
55
+ before(beforeFn) {
56
+ beforeHooks[beforeHooks.length] = beforeFn;
57
+ },
58
+ beforeEach(beforeEachFn) {
59
+ moduleContext.beforeEachHooks[moduleContext.beforeEachHooks.length] = beforeEachFn;
60
+ },
61
+ afterEach(afterEachFn) {
62
+ moduleContext.afterEachHooks[moduleContext.afterEachHooks.length] = afterEachFn;
63
+ },
64
+ after(afterFn) {
65
+ afterHooks[afterHooks.length] = afterFn;
66
+ }
67
+ }, { moduleName, options: runtimeOptions });
68
+
69
+ ModuleContext.currentModuleChain.pop();
70
+ });
71
+ }
@@ -0,0 +1,37 @@
1
+ import { it } from 'node:test';
2
+ import TestContext from '../shared/test-context.js';
3
+ import ModuleContext from '../shared/module-context.js';
4
+
5
+ export default function test(testName, runtimeOptions, testContent) {
6
+ let moduleContext = ModuleContext.lastModule;
7
+ if (!moduleContext) {
8
+ throw new Error(`Test '${testName}' called outside of module context.`);
9
+ }
10
+
11
+ let targetRuntimeOptions = testContent ? runtimeOptions : {};
12
+ let targetTestContent = testContent ? testContent : runtimeOptions;
13
+ let context = new TestContext(testName, moduleContext);
14
+
15
+ return it(testName, { concurrency: true, ...targetRuntimeOptions }, async function () {
16
+ let result;
17
+ for (let module of context.module.moduleChain) {
18
+ for (let hook of module.beforeEachHooks) {
19
+ await hook.call(context, context.assert);
20
+ }
21
+ }
22
+
23
+ result = await targetTestContent.call(context, context.assert, { testName, options: runtimeOptions });
24
+
25
+ await context.assert.waitForAsyncOps();
26
+
27
+ for (let i = context.module.moduleChain.length - 1; i >= 0; i--) {
28
+ let module = context.module.moduleChain[i];
29
+ for (let j = module.afterEachHooks.length - 1; j >= 0; j--) {
30
+ await module.afterEachHooks[j].call(context, context.assert);
31
+ }
32
+ }
33
+
34
+ return result;
35
+ });
36
+ }
37
+
@@ -1,89 +1,130 @@
1
- import { AssertionError as DenoAssertionError, assertRejects, assertThrows } from "https://deno.land/std@0.192.0/testing/asserts.ts";
2
1
  import '../../vendor/qunit.js';
3
2
  import { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException } from '../shared/index.js';
4
3
  import util from 'node:util';
5
4
 
6
- export class AssertionError extends DenoAssertionError {
7
- constructor(object) {
8
- super(object.message);
5
+ // More: contexts needed for timeout
6
+ // NOTE: Another approach for a global report Make this._assertions.set(this.currentTest, (this._assertions.get(this.currentTest) || 0) + 1); for pushResult
7
+ // NOTE: This should *always* be a singleton(?), passed around as an argument for hooks. Seems difficult with concurrency. Singleton needs to be a concurrent data structure.
8
+
9
+ export default class Assert {
10
+ static QUnit;
11
+ static AssertionError;
12
+
13
+ constructor(module, test) {
14
+ this.test = test || module.context;
9
15
  }
10
- }
11
16
 
12
- // NOTE: Maybe do the expect, steps in some object, and also do timeout and async(?)
13
- export default {
14
- _steps: [],
15
- timeout() {
16
- return true; // NOTE: NOT implemented
17
- },
18
- step(value = '') {
19
- this._steps.push(value);
20
- },
21
- verifySteps(steps, message = 'Verify steps failed!') {
22
- const result = this.deepEqual(this._steps, steps, message);
17
+ _incrementAssertionCount() {
18
+ this.test.totalExecutedAssertions++;
19
+ }
20
+ timeout(number) {
21
+ if (!Number.isInteger(number) || number < 0) {
22
+ throw new Error('assert.timeout() expects a positive integer.');
23
+ }
24
+
25
+ this.test.timeout = number;
26
+ }
27
+ step(message) {
28
+ let assertionMessage = message;
29
+ let result = !!message;
30
+
31
+ this.test.steps.push(message);
32
+
33
+ if (typeof message === 'undefined' || message === '') {
34
+ assertionMessage = 'You must provide a message to assert.step';
35
+ } else if (typeof message !== 'string') {
36
+ assertionMessage = 'You must provide a string value to assert.step';
37
+ result = false;
38
+ }
23
39
 
24
- this._steps.length = 0;
40
+ this.pushResult({
41
+ result,
42
+ message: assertionMessage
43
+ });
44
+ }
45
+ verifySteps(steps, message = 'Verify steps failed!') {
46
+ this.deepEqual(this.test.steps, steps, message);
47
+ this.test.steps.length = 0;
48
+ }
49
+ expect(number) {
50
+ if (!Number.isInteger(number) || number < 0) {
51
+ throw new Error('assert.expect() expects a positive integer.');
52
+ }
25
53
 
26
- return result;
27
- },
28
- expect() {
29
- return () => {}; // NOTE: NOT implemented
30
- },
54
+ this.test.expectedAssertionCount = number;
55
+ }
31
56
  async() {
32
- return () => {}; // NOTE: noop, node should have sanitizeResources
33
- },
57
+ let resolveFn;
58
+ let done = new Promise(resolve => { resolveFn = resolve; });
59
+
60
+ this.test.asyncOps.push(done);
61
+
62
+ return () => { resolveFn(); };
63
+ }
64
+ async waitForAsyncOps() {
65
+ return Promise.all(this.test.asyncOps);
66
+ }
34
67
  pushResult(resultInfo = {}) {
35
- if (!result) {
36
- throw new AssertionError({
68
+ this._incrementAssertionCount();
69
+ if (!resultInfo.result) {
70
+ throw new Assert.AssertionError({
37
71
  actual: resultInfo.actual,
38
72
  expected: resultInfo.expected,
39
- message: result.Infomessage || 'Custom assertion failed!',
73
+ message: resultInfo.message || 'Custom assertion failed!',
40
74
  stackStartFn: this.pushResult,
41
75
  });
42
76
  }
43
- },
77
+
78
+ return this;
79
+ }
44
80
  ok(state, message) {
81
+ this._incrementAssertionCount();
45
82
  if (!state) {
46
- throw new AssertionError({
83
+ throw new Assert.AssertionError({
47
84
  actual: state,
48
85
  expected: true,
49
86
  message: message || `Expected argument to be truthy, it was: ${inspect(state)}`,
50
87
  stackStartFn: this.ok,
51
88
  });
52
89
  }
53
- },
90
+ }
54
91
  notOk(state, message) {
92
+ this._incrementAssertionCount();
55
93
  if (state) {
56
- throw new AssertionError({
94
+ throw new Assert.AssertionError({
57
95
  actual: state,
58
96
  expected: false,
59
97
  message: message || `Expected argument to be falsy, it was: ${inspect(state)}`,
60
98
  stackStartFn: this.notOk,
61
99
  });
62
100
  }
63
- },
101
+ }
64
102
  true(state, message) {
103
+ this._incrementAssertionCount();
65
104
  if (state !== true) {
66
- throw new AssertionError({
105
+ throw new Assert.AssertionError({
67
106
  actual: state,
68
107
  expected: true,
69
108
  message: message || `Expected argument to be true, it was: ${inspect(state)}`,
70
109
  stackStartFn: this.true,
71
110
  });
72
111
  }
73
- },
112
+ }
74
113
  false(state, message) {
114
+ this._incrementAssertionCount();
75
115
  if (state !== false) {
76
- throw new AssertionError({
116
+ throw new Assert.AssertionError({
77
117
  actual: state,
78
118
  expected: true,
79
119
  message: message || `Expected argument to be false, it was: ${inspect(state)}`,
80
120
  stackStartFn: this.false,
81
121
  });
82
122
  }
83
- },
123
+ }
84
124
  equal(actual, expected, message) {
125
+ this._incrementAssertionCount();
85
126
  if (actual != expected) {
86
- throw new AssertionError({
127
+ throw new Assert.AssertionError({
87
128
  actual,
88
129
  expected,
89
130
  message: message || `Expected: ${defaultMessage(actual, 'should equal to:', expected)}`,
@@ -91,10 +132,11 @@ export default {
91
132
  stackStartFn: this.equal,
92
133
  });
93
134
  }
94
- },
135
+ }
95
136
  notEqual(actual, expected, message) {
137
+ this._incrementAssertionCount();
96
138
  if (actual == expected) {
97
- throw new AssertionError({
139
+ throw new Assert.AssertionError({
98
140
  actual,
99
141
  expected,
100
142
  operator: '!=',
@@ -102,58 +144,63 @@ export default {
102
144
  stackStartFn: this.notEqual,
103
145
  });
104
146
  }
105
- },
147
+ }
106
148
  propEqual(actual, expected, message) {
149
+ this._incrementAssertionCount();
107
150
  let targetActual = objectValues(actual);
108
151
  let targetExpected = objectValues(expected);
109
- if (!window.QUnit.equiv(targetActual, targetExpected)) {
110
- throw new AssertionError({
152
+ if (!Assert.QUnit.equiv(targetActual, targetExpected)) {
153
+ throw new Assert.AssertionError({
111
154
  actual: targetActual,
112
155
  expected: targetExpected,
113
156
  message: message || `Expected properties to be propEqual: ${defaultMessage(targetActual, 'should propEqual to:', targetExpected)}`,
114
157
  stackStartFn: this.propEqual,
115
158
  });
116
159
  }
117
- },
160
+ }
118
161
  notPropEqual(actual, expected, message) {
162
+ this._incrementAssertionCount();
119
163
  let targetActual = objectValues(actual);
120
164
  let targetExpected = objectValues(expected);
121
- if (window.QUnit.equiv(targetActual, targetExpected)) {
122
- throw new AssertionError({
165
+ if (Assert.QUnit.equiv(targetActual, targetExpected)) {
166
+ throw new Assert.AssertionError({
123
167
  actual: targetActual,
124
168
  expected: targetExpected,
125
169
  message: message || `Expected properties to NOT be propEqual: ${defaultMessage(targetActual, 'should notPropEqual to:', targetExpected)}`,
126
170
  stackStartFn: this.notPropEqual,
127
171
  });
128
172
  }
129
- },
173
+ }
130
174
  propContains(actual, expected, message) {
175
+ this._incrementAssertionCount();
131
176
  let targetActual = objectValuesSubset(actual, expected);
132
177
  let targetExpected = objectValues(expected, false);
133
- if (!window.QUnit.equiv(targetActual, targetExpected)) {
134
- throw new AssertionError({
178
+ if (!Assert.QUnit.equiv(targetActual, targetExpected)) {
179
+ throw new Assert.AssertionError({
135
180
  actual: targetActual,
136
181
  expected: targetExpected,
137
182
  message: message || `propContains assertion fail on: ${defaultMessage(targetActual, 'should propContains to:', targetExpected)}`,
138
183
  stackStartFn: this.propContains,
139
184
  });
140
185
  }
141
- },
186
+ }
142
187
  notPropContains(actual, expected, message) {
188
+ this._incrementAssertionCount();
143
189
  let targetActual = objectValuesSubset(actual, expected);
144
190
  let targetExpected = objectValues(expected);
145
- if (window.QUnit.equiv(targetActual, targetExpected)) {
146
- throw new AssertionError({
191
+ if (Assert.QUnit.equiv(targetActual, targetExpected)) {
192
+ throw new Assert.AssertionError({
147
193
  actual: targetActual,
148
194
  expected: targetExpected,
149
195
  message: message || `notPropContains assertion fail on: ${defaultMessage(targetActual, 'should notPropContains of:', targetExpected)}`,
150
196
  stackStartFn: this.notPropContains,
151
197
  });
152
198
  }
153
- },
199
+ }
154
200
  deepEqual(actual, expected, message) {
155
- if (!window.QUnit.equiv(actual, expected)) {
156
- throw new AssertionError({
201
+ this._incrementAssertionCount();
202
+ if (!Assert.QUnit.equiv(actual, expected)) {
203
+ throw new Assert.AssertionError({
157
204
  actual,
158
205
  expected,
159
206
  message: message || `Expected values to be deepEqual: ${defaultMessage(actual, 'should deepEqual to:', expected)}`,
@@ -161,10 +208,11 @@ export default {
161
208
  stackStartFn: this.deepEqual,
162
209
  });
163
210
  }
164
- },
211
+ }
165
212
  notDeepEqual(actual, expected, message) {
166
- if (window.QUnit.equiv(actual, expected)) {
167
- throw new AssertionError({
213
+ this._incrementAssertionCount();
214
+ if (Assert.QUnit.equiv(actual, expected)) {
215
+ throw new Assert.AssertionError({
168
216
  actual,
169
217
  expected,
170
218
  message: message || `Expected values to be NOT deepEqual: ${defaultMessage(actual, 'should notDeepEqual to:', expected)}`,
@@ -172,10 +220,11 @@ export default {
172
220
  stackStartFn: this.notDeepEqual,
173
221
  });
174
222
  }
175
- },
223
+ }
176
224
  strictEqual(actual, expected, message) {
225
+ this._incrementAssertionCount();
177
226
  if (actual !== expected) {
178
- throw new AssertionError({
227
+ throw new Assert.AssertionError({
179
228
  actual,
180
229
  expected,
181
230
  message: message || `Expected: ${defaultMessage(actual, 'is strictEqual to:', expected)}`,
@@ -183,10 +232,11 @@ export default {
183
232
  stackStartFn: this.strictEqual,
184
233
  });
185
234
  }
186
- },
235
+ }
187
236
  notStrictEqual(actual, expected, message) {
237
+ this._incrementAssertionCount();
188
238
  if (actual === expected) {
189
- throw new AssertionError({
239
+ throw new Assert.AssertionError({
190
240
  actual,
191
241
  expected,
192
242
  message: message || `Expected: ${defaultMessage(actual, 'is notStrictEqual to:', expected)}`,
@@ -194,11 +244,12 @@ export default {
194
244
  stackStartFn: this.notStrictEqual,
195
245
  });
196
246
  }
197
- },
247
+ }
198
248
  throws(blockFn, expectedInput, assertionMessage) {
249
+ this?._incrementAssertionCount();
199
250
  let [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
200
251
  if (typeof blockFn !== 'function') {
201
- throw new AssertionError({
252
+ throw new Assert.AssertionError({
202
253
  actual: blockFn,
203
254
  expected: Function,
204
255
  message: 'The value provided to `assert.throws` was not a function.',
@@ -211,7 +262,7 @@ export default {
211
262
  } catch (error) {
212
263
  let validation = validateException(error, expected, message);
213
264
  if (validation.result === false) {
214
- throw new AssertionError({
265
+ throw new Assert.AssertionError({
215
266
  actual: validation.result,
216
267
  expected: validation.expected,
217
268
  message: validation.message,
@@ -222,18 +273,19 @@ export default {
222
273
  return;
223
274
  }
224
275
 
225
- throw new AssertionError({
276
+ throw new Assert.AssertionError({
226
277
  actual: blockFn,
227
278
  expected: expected,
228
279
  message: 'Function passed to `assert.throws` did not throw an exception!',
229
280
  stackStartFn: this.throws,
230
281
  });
231
- },
282
+ }
232
283
  async rejects(promise, expectedInput, assertionMessage) {
284
+ this._incrementAssertionCount();
233
285
  let [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
234
286
  let then = promise && promise.then;
235
287
  if (typeof then !== 'function') {
236
- throw new AssertionError({
288
+ throw new Assert.AssertionError({
237
289
  actual: promise,
238
290
  expected: expected,
239
291
  message: 'The value provided to `assert.rejects` was not a promise!',
@@ -243,7 +295,7 @@ export default {
243
295
 
244
296
  try {
245
297
  await promise;
246
- throw new AssertionError({
298
+ throw new Assert.AssertionError({
247
299
  actual: promise,
248
300
  expected: expected,
249
301
  message: 'The promise returned by the `assert.rejects` callback did not reject!',
@@ -252,7 +304,7 @@ export default {
252
304
  } catch (error) {
253
305
  let validation = validateException(error, expected, message);
254
306
  if (validation.result === false) {
255
- throw new AssertionError({
307
+ throw new Assert.AssertionError({
256
308
  actual: validation.result,
257
309
  expected: validation.expected,
258
310
  message: validation.message,
@@ -276,4 +328,3 @@ ${inspect(expected)}`
276
328
  function inspect(value) {
277
329
  return util.inspect(value, { depth: 10, colors: true, compact: false });
278
330
  }
279
-
@@ -0,0 +1,47 @@
1
+ import TestContext from './test-context.js';
2
+
3
+ export default class ModuleContext {
4
+ static Assert;
5
+ static currentModuleChain = [];
6
+
7
+ static get lastModule() {
8
+ return this.currentModuleChain[this.currentModuleChain.length - 1];
9
+ }
10
+
11
+ context = new TestContext();
12
+
13
+ #tests = [];
14
+ get tests() {
15
+ return this.#tests;
16
+ }
17
+
18
+ #beforeEachHooks = [];
19
+ get beforeEachHooks() {
20
+ return this.#beforeEachHooks;
21
+ }
22
+
23
+ #afterEachHooks = [];
24
+ get afterEachHooks() {
25
+ return this.#afterEachHooks;
26
+ }
27
+
28
+ #moduleChain = [];
29
+ get moduleChain() {
30
+ return this.#moduleChain;
31
+ }
32
+ set moduleChain(value) {
33
+ this.#moduleChain = value;
34
+ }
35
+
36
+ constructor(name) {
37
+ let parentModule = ModuleContext.currentModuleChain[ModuleContext.currentModuleChain.length - 1];
38
+
39
+ ModuleContext.currentModuleChain.push(this);
40
+
41
+ this.moduleChain = ModuleContext.currentModuleChain.slice(0);
42
+ this.name = parentModule ? `${parentModule.name} > ${name}` : name;
43
+ this.assert = new ModuleContext.Assert(this);
44
+
45
+ return Object.freeze(this);
46
+ }
47
+ }
@@ -0,0 +1,102 @@
1
+ export default class TestContext {
2
+ static Assert;
3
+
4
+ #name;
5
+ get name() {
6
+ return this.#name;
7
+ }
8
+ set name(value) {
9
+ this.#name = value;
10
+ }
11
+
12
+ #module;
13
+ get module() {
14
+ return this.#module;
15
+ }
16
+ set module(value) {
17
+ this.#module = value;
18
+ }
19
+
20
+ #asyncOps = [];
21
+ get asyncOps() {
22
+ return this.#asyncOps;
23
+ }
24
+ set asyncOps(value) {
25
+ this.#asyncOps = value;
26
+ }
27
+
28
+ #assert;
29
+ get assert() {
30
+ return this.#assert;
31
+ }
32
+ set assert(value) {
33
+ this.#assert = value;
34
+ }
35
+
36
+ #timeout;
37
+ get timeout() {
38
+ return this.#timeout;
39
+ }
40
+ set timeout(value) {
41
+ this.#timeout = value;
42
+ }
43
+
44
+ #steps = [];
45
+ get steps() {
46
+ return this.#steps;
47
+ }
48
+ set steps(value) {
49
+ this.#steps = value;
50
+ }
51
+
52
+ #expectedAssertionCount;
53
+ get expectedAssertionCount() {
54
+ return this.#expectedAssertionCount;
55
+ }
56
+ set expectedAssertionCount(value) {
57
+ this.#expectedAssertionCount = value;
58
+ }
59
+
60
+ #totalExecutedAssertions = 0;
61
+ get totalExecutedAssertions() {
62
+ return this.#totalExecutedAssertions;
63
+ }
64
+ set totalExecutedAssertions(value) {
65
+ this.#totalExecutedAssertions = value;
66
+ }
67
+
68
+ constructor(name, moduleContext) {
69
+ if (moduleContext) {
70
+ this.name = `${moduleContext.name} | ${name}`;
71
+ this.module = moduleContext;
72
+ this.module.tests.push(this);
73
+ this.assert = new TestContext.Assert(moduleContext, this);
74
+ }
75
+ }
76
+
77
+ finish() {
78
+ if (this.totalExecutedAssertions === 0) {
79
+ this.assert.pushResult({
80
+ result: false,
81
+ actual: this.totalExecutedAssertions,
82
+ expected: '> 0',
83
+ message: `Expected at least one assertion to be run for test: ${this.name}`,
84
+ });
85
+ } else if (this.steps.length > 0) {
86
+ this.assert.pushResult({
87
+ result: false,
88
+ actual: this.steps,
89
+ expected: [],
90
+ message: `Expected assert.verifySteps() to be called before end of test after using assert.step(). Unverified steps: ${this.steps.join(', ')}`,
91
+ });
92
+ } else if (this.expectedAssertionCount && this.expectedAssertionCount !== this.totalExecutedAssertions) {
93
+ this.assert.pushResult({
94
+ result: false,
95
+ actual: this.totalExecutedAssertions,
96
+ expected: this.expectedAssertionCount,
97
+ message: `Expected ${this.expectedAssertionCount} assertions, but ${this.totalExecutedAssertions} were run for test: ${this.name}`,
98
+ });
99
+ }
100
+ }
101
+ }
102
+
@@ -1,272 +0,0 @@
1
- import QUnit from '../../vendor/qunit.js';
2
- import { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException } from '../shared/index.js';
3
- import assert, { AssertionError } from 'node:assert';
4
- import util from 'node:util';
5
-
6
- // NOTE: Maybe do the expect, steps in some object, and also do timeout and async(?)
7
- export default {
8
- _steps: [],
9
- timeout() {
10
- return true; // NOTE: NOT implemented
11
- },
12
- step(value = '') {
13
- this._steps.push(value);
14
- },
15
- verifySteps(steps, message = 'Verify steps failed!') {
16
- const result = this.deepEqual(this._steps, steps, message);
17
-
18
- this._steps.length = 0;
19
-
20
- return result;
21
- },
22
- expect() {
23
- return () => {}; // NOTE: NOT implemented
24
- },
25
- async() {
26
- return () => {}; // NOTE: noop, node should have sanitizeResources
27
- },
28
- pushResult(resultInfo = {}) {
29
- if (!result) {
30
- throw new AssertionError({
31
- actual: resultInfo.actual,
32
- expected: resultInfo.expected,
33
- message: result.Infomessage || 'Custom assertion failed!',
34
- stackStartFn: this.pushResult,
35
- });
36
- }
37
- },
38
- ok(state, message) {
39
- if (!state) {
40
- throw new AssertionError({
41
- actual: state,
42
- expected: true,
43
- message: message || `Expected argument to be truthy, it was: ${inspect(state)}`,
44
- stackStartFn: this.ok,
45
- });
46
- }
47
- },
48
- notOk(state, message) {
49
- if (state) {
50
- throw new AssertionError({
51
- actual: state,
52
- expected: false,
53
- message: message || `Expected argument to be falsy, it was: ${inspect(state)}`,
54
- stackStartFn: this.notOk,
55
- });
56
- }
57
- },
58
- true(state, message) {
59
- if (state !== true) {
60
- throw new AssertionError({
61
- actual: state,
62
- expected: true,
63
- message: message || `Expected argument to be true, it was: ${inspect(state)}`,
64
- stackStartFn: this.true,
65
- });
66
- }
67
- },
68
- false(state, message) {
69
- if (state !== false) {
70
- throw new AssertionError({
71
- actual: state,
72
- expected: true,
73
- message: message || `Expected argument to be false, it was: ${inspect(state)}`,
74
- stackStartFn: this.false,
75
- });
76
- }
77
- },
78
- equal(actual, expected, message) {
79
- if (actual != expected) {
80
- throw new AssertionError({
81
- actual,
82
- expected,
83
- message: message || `Expected: ${defaultMessage(actual, 'should equal to:', expected)}`,
84
- operator: '==',
85
- stackStartFn: this.equal,
86
- });
87
- }
88
- },
89
- notEqual(actual, expected, message) {
90
- if (actual == expected) {
91
- throw new AssertionError({
92
- actual,
93
- expected,
94
- operator: '!=',
95
- message: message || `Expected: ${defaultMessage(actual, 'should notEqual to:', expected)}`,
96
- stackStartFn: this.notEqual,
97
- });
98
- }
99
- },
100
- propEqual(actual, expected, message) {
101
- let targetActual = objectValues(actual);
102
- let targetExpected = objectValues(expected);
103
- if (!QUnit.equiv(targetActual, targetExpected)) {
104
- throw new AssertionError({
105
- actual: targetActual,
106
- expected: targetExpected,
107
- message: message || `Expected properties to be propEqual: ${defaultMessage(targetActual, 'should propEqual to:', targetExpected)}`,
108
- stackStartFn: this.propEqual,
109
- });
110
- }
111
- },
112
- notPropEqual(actual, expected, message) {
113
- let targetActual = objectValues(actual);
114
- let targetExpected = objectValues(expected);
115
- if (QUnit.equiv(targetActual, targetExpected)) {
116
- throw new AssertionError({
117
- actual: targetActual,
118
- expected: targetExpected,
119
- message: message || `Expected properties to NOT be propEqual: ${defaultMessage(targetActual, 'should notPropEqual to:', targetExpected)}`,
120
- stackStartFn: this.notPropEqual,
121
- });
122
- }
123
- },
124
- propContains(actual, expected, message) {
125
- let targetActual = objectValuesSubset(actual, expected);
126
- let targetExpected = objectValues(expected, false);
127
- if (!QUnit.equiv(targetActual, targetExpected)) {
128
- throw new AssertionError({
129
- actual: targetActual,
130
- expected: targetExpected,
131
- message: message || `propContains assertion fail on: ${defaultMessage(targetActual, 'should propContains to:', targetExpected)}`,
132
- stackStartFn: this.propContains,
133
- });
134
- }
135
- },
136
- notPropContains(actual, expected, message) {
137
- let targetActual = objectValuesSubset(actual, expected);
138
- let targetExpected = objectValues(expected);
139
- if (QUnit.equiv(targetActual, targetExpected)) {
140
- throw new AssertionError({
141
- actual: targetActual,
142
- expected: targetExpected,
143
- message: message || `notPropContains assertion fail on: ${defaultMessage(targetActual, 'should notPropContains of:', targetExpected)}`,
144
- stackStartFn: this.notPropContains,
145
- });
146
- }
147
- },
148
- deepEqual(actual, expected, message) {
149
- if (!QUnit.equiv(actual, expected)) {
150
- throw new AssertionError({
151
- actual,
152
- expected,
153
- message: message || `Expected values to be deepEqual: ${defaultMessage(actual, 'should deepEqual to:', expected)}`,
154
- operator: 'deepEqual',
155
- stackStartFn: this.deepEqual,
156
- });
157
- }
158
- },
159
- notDeepEqual(actual, expected, message) {
160
- if (QUnit.equiv(actual, expected)) {
161
- throw new AssertionError({
162
- actual,
163
- expected,
164
- message: message || `Expected values to be NOT deepEqual: ${defaultMessage(actual, 'should notDeepEqual to:', expected)}`,
165
- operator: 'notDeepEqual',
166
- stackStartFn: this.notDeepEqual,
167
- });
168
- }
169
- },
170
- strictEqual(actual, expected, message) {
171
- if (actual !== expected) {
172
- throw new AssertionError({
173
- actual,
174
- expected,
175
- message: message || `Expected: ${defaultMessage(actual, 'is strictEqual to:', expected)}`,
176
- operator: 'strictEqual',
177
- stackStartFn: this.strictEqual,
178
- });
179
- }
180
- },
181
- notStrictEqual(actual, expected, message) {
182
- if (actual === expected) {
183
- throw new AssertionError({
184
- actual,
185
- expected,
186
- message: message || `Expected: ${defaultMessage(actual, 'is notStrictEqual to:', expected)}`,
187
- operator: 'notStrictEqual',
188
- stackStartFn: this.notStrictEqual,
189
- });
190
- }
191
- },
192
- throws(blockFn, expectedInput, assertionMessage) {
193
- let [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
194
- if (typeof blockFn !== 'function') {
195
- throw new AssertionError({
196
- actual: blockFn,
197
- expected: Function,
198
- message: 'The value provided to `assert.throws` was not a function.',
199
- stackStartFn: this.throws,
200
- });
201
- }
202
-
203
- try {
204
- blockFn();
205
- } catch (error) {
206
- let validation = validateException(error, expected, message);
207
- if (validation.result === false) {
208
- throw new AssertionError({
209
- actual: validation.result,
210
- expected: validation.expected,
211
- message: validation.message,
212
- stackStartFn: this.throws,
213
- });
214
- }
215
-
216
- return;
217
- }
218
-
219
- throw new AssertionError({
220
- actual: blockFn,
221
- expected: expected,
222
- message: 'Function passed to `assert.throws` did not throw an exception!',
223
- stackStartFn: this.throws,
224
- });
225
- },
226
- async rejects(promise, expectedInput, assertionMessage) {
227
- let [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
228
- let then = promise && promise.then;
229
- if (typeof then !== 'function') {
230
- throw new AssertionError({
231
- actual: promise,
232
- expected: expected,
233
- message: 'The value provided to `assert.rejects` was not a promise!',
234
- stackStartFn: this.rejects,
235
- });
236
- }
237
-
238
- try {
239
- await promise;
240
- throw new AssertionError({
241
- actual: promise,
242
- expected: expected,
243
- message: 'The promise returned by the `assert.rejects` callback did not reject!',
244
- stackStartFn: this.rejects,
245
- });
246
- } catch (error) {
247
- let validation = validateException(error, expected, message);
248
- if (validation.result === false) {
249
- throw new AssertionError({
250
- actual: validation.result,
251
- expected: validation.expected,
252
- message: validation.message,
253
- stackStartFn: this.rejects,
254
- });
255
- }
256
- }
257
- }
258
- };
259
-
260
- function defaultMessage(actual, description, expected) {
261
- return `
262
-
263
- ${inspect(actual)}
264
-
265
- ${description}
266
-
267
- ${inspect(expected)}`
268
- }
269
-
270
- function inspect(value) {
271
- return util.inspect(value, { depth: 10, colors: true, compact: false });
272
- }