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 +31 -3
- package/shims/deno/index.js +22 -43
- package/shims/deno/module.js +71 -0
- package/shims/deno/test.js +37 -0
- package/shims/node/index.js +17 -70
- package/shims/node/module.js +71 -0
- package/shims/node/test.js +37 -0
- package/shims/{deno → shared}/assert.js +121 -70
- package/shims/shared/module-context.js +47 -0
- package/shims/shared/test-context.js +102 -0
- package/shims/node/assert.js +0 -272
package/package.json
CHANGED
|
@@ -1,14 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qunitx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
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.
|
|
92
|
+
"node": "20.5.0"
|
|
65
93
|
},
|
|
66
94
|
"prettier": {
|
|
67
95
|
"printWidth": 100,
|
package/shims/deno/index.js
CHANGED
|
@@ -1,48 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
Assert.QUnit = QUnit;
|
|
16
|
+
Assert.AssertionError = AssertionError;
|
|
17
|
+
ModuleContext.Assert = Assert;
|
|
18
|
+
TestContext.Assert = Assert;
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
Object.freeze(Assert);
|
|
21
|
+
Object.freeze(ModuleContext);
|
|
22
|
+
Object.freeze(TestContext);
|
|
31
23
|
|
|
32
|
-
|
|
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,
|
|
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
|
+
|
package/shims/node/index.js
CHANGED
|
@@ -1,75 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
let targetModuleContent = moduleContent ? moduleContent : runtimeOptions;
|
|
9
|
+
Assert.QUnit = QUnit;
|
|
10
|
+
Assert.AssertionError = AssertionError;
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
moduleName,
|
|
11
|
-
options: runtimeOptions
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
}
|
|
12
|
+
ModuleContext.Assert = Assert;
|
|
13
|
+
TestContext.Assert = Assert;
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
Object.freeze(Assert);
|
|
16
|
+
Object.freeze(ModuleContext);
|
|
17
|
+
Object.freeze(TestContext);
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
}
|
|
19
|
+
export const module = Module;
|
|
20
|
+
export const test = Test;
|
|
24
21
|
|
|
25
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
timeout() {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
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
|
-
|
|
27
|
-
}
|
|
28
|
-
expect() {
|
|
29
|
-
return () => {}; // NOTE: NOT implemented
|
|
30
|
-
},
|
|
54
|
+
this.test.expectedAssertionCount = number;
|
|
55
|
+
}
|
|
31
56
|
async() {
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
68
|
+
this._incrementAssertionCount();
|
|
69
|
+
if (!resultInfo.result) {
|
|
70
|
+
throw new Assert.AssertionError({
|
|
37
71
|
actual: resultInfo.actual,
|
|
38
72
|
expected: resultInfo.expected,
|
|
39
|
-
message:
|
|
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 (!
|
|
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 (
|
|
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 (!
|
|
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 (
|
|
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
|
-
|
|
156
|
-
|
|
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
|
-
|
|
167
|
-
|
|
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
|
+
|
package/shims/node/assert.js
DELETED
|
@@ -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
|
-
}
|