qunitx 0.12.2 → 0.12.4
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/README.md +13 -4
- package/package.json +10 -4
- package/scripts/check-coverage.js +15 -0
- package/shims/deno/module.js +35 -8
- package/shims/deno/test.js +38 -9
- package/shims/node/module.js +8 -8
- package/shims/node/test.js +8 -8
- package/shims/shared/assert.js +15 -15
- package/shims/shared/index.js +16 -22
- package/shims/shared/module-context.js +1 -1
- package/Makefile +0 -28
package/README.md
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# QUnitX
|
|
4
|
+
|
|
1
5
|
[](https://github.com/izelnakri/qunitx/actions/workflows/ci.yml)
|
|
6
|
+
[](https://codecov.io/gh/izelnakri/qunitx)
|
|
2
7
|
[](https://www.npmjs.com/package/qunitx)
|
|
3
8
|
[](https://www.npmjs.com/package/qunitx)
|
|
4
9
|
[](LICENSE)
|
|
5
10
|
[](https://github.com/izelnakri/qunitx/issues)
|
|
6
11
|
[](https://github.com/sponsors/izelnakri)
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
</div>
|
|
9
14
|
|
|
10
15
|
**The oldest, most battle-tested JavaScript test API — now universal.**
|
|
11
16
|
|
|
@@ -36,6 +41,10 @@ ecosystem:
|
|
|
36
41
|
QUnitX wraps this API to work with **Node.js's built-in `node:test` runner** and
|
|
37
42
|
**Deno's native test runner** — no Jest, Vitest, or other framework needed.
|
|
38
43
|
|
|
44
|
+
QUnit includes the fastest assertion and test runtime in JS world. I've previously contributed to some [speed optimizations](https://qunitjs.com/blog/2022/02/15/qunit-2-18-0/) to QUnit, we benchmark every possible thing to make it the fastest test
|
|
45
|
+
runtime, faster than node.js and deno default assertions in most cases. Therefore I consider myself very objective
|
|
46
|
+
when I say QUnit(X) is the best JS/TS testing tool out there.
|
|
47
|
+
|
|
39
48
|
---
|
|
40
49
|
|
|
41
50
|
## Demo
|
|
@@ -51,14 +60,12 @@ Live browser UI example (click to see filterable QUnit test suite):
|
|
|
51
60
|
|
|
52
61
|
[objectmodel.js.org/test/?moduleId=6e15ed5f](https://objectmodel.js.org/test/?moduleId=6e15ed5f&moduleId=950ec9c5)
|
|
53
62
|
|
|
54
|
-

|
|
55
|
-
|
|
56
63
|
---
|
|
57
64
|
|
|
58
65
|
## Installation
|
|
59
66
|
|
|
60
67
|
```sh
|
|
61
|
-
npm install qunitx
|
|
68
|
+
npm install qunitx --save-dev
|
|
62
69
|
```
|
|
63
70
|
|
|
64
71
|
Requires **Node.js >= 22** (LTS) or **Deno >= 2**.
|
|
@@ -204,6 +211,8 @@ familiar browser UI with zero extra layers.
|
|
|
204
211
|
|
|
205
212
|
## Code coverage
|
|
206
213
|
|
|
214
|
+
Probably c8 isn't even needed since qunitx runs as a dependency(rather than runtime) on node.js and deno.
|
|
215
|
+
|
|
207
216
|
```sh
|
|
208
217
|
# Node (any c8-compatible reporter)
|
|
209
218
|
npx c8 node --test test/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qunitx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.4",
|
|
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",
|
|
@@ -63,8 +63,10 @@
|
|
|
63
63
|
"url": "git+https://github.com/izelnakri/qunitx.git"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
|
-
"
|
|
67
|
-
"
|
|
66
|
+
"format": "prettier --check \"test/**/*.js\" \"*.js\" \"package.json\"",
|
|
67
|
+
"format:fix": "prettier --write \"test/**/*.js\" \"*.js\" \"package.json\"",
|
|
68
|
+
"lint": "deno lint shims/",
|
|
69
|
+
"lint:docs": "deno doc --lint shims/deno/module.js shims/deno/test.js",
|
|
68
70
|
"build": "node build.js",
|
|
69
71
|
"run:all": "npm run run:node && npm run run:deno",
|
|
70
72
|
"run:node": "node --test test/helpers/passing-tests.js && node --test test/helpers/failing-tests.js",
|
|
@@ -77,7 +79,11 @@
|
|
|
77
79
|
"test:dev": "npm run test | tee test-output.log",
|
|
78
80
|
"test:browser": "qunitx test/index.js --debug",
|
|
79
81
|
"test:deno": "deno test --allow-read --allow-env --allow-run test/index.js",
|
|
80
|
-
"test:
|
|
82
|
+
"test:doctests": "deno test --doc --allow-env --allow-read shims/deno/module.js shims/deno/test.js",
|
|
83
|
+
"test:node": "node --test test/index.js",
|
|
84
|
+
"coverage": "deno test --coverage=tmp/coverage --allow-read --allow-env --allow-run test/index.js && deno coverage --lcov --output=tmp/coverage/lcov.info --include='shims/' tmp/coverage && node scripts/check-coverage.js",
|
|
85
|
+
"coverage:report": "npm run coverage && deno coverage --html --include='shims/' tmp/coverage",
|
|
86
|
+
"docs": "deno doc --html --name=\"QUnitX\" --output=docs/src shims/deno/index.js"
|
|
81
87
|
},
|
|
82
88
|
"devDependencies": {
|
|
83
89
|
"prettier": "^3.8.1",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
const THRESHOLD = 85;
|
|
4
|
+
const lcov = await readFile('tmp/coverage/lcov.info', 'utf8');
|
|
5
|
+
|
|
6
|
+
const lh = [...lcov.matchAll(/^LH:(\d+)/gm)].reduce((s, m) => s + parseInt(m[1]), 0);
|
|
7
|
+
const lf = [...lcov.matchAll(/^LF:(\d+)/gm)].reduce((s, m) => s + parseInt(m[1]), 0);
|
|
8
|
+
const pct = lf > 0 ? (lh / lf) * 100 : 0;
|
|
9
|
+
|
|
10
|
+
console.log(`Coverage: ${pct.toFixed(1)}% (${lh}/${lf} lines)`);
|
|
11
|
+
|
|
12
|
+
if (pct < THRESHOLD) {
|
|
13
|
+
console.error(`Error: coverage ${pct.toFixed(1)}% is below the ${THRESHOLD}% threshold.`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
package/shims/deno/module.js
CHANGED
|
@@ -5,14 +5,41 @@ import ModuleContext from '../shared/module-context.js';
|
|
|
5
5
|
// NOTE: QUnit expect() logic is buggy in nested modules
|
|
6
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
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Defines a test module (suite) for Deno's BDD test runner.
|
|
10
|
+
*
|
|
11
|
+
* Wraps `describe()` from `@std/testing/bdd` and sets up the QUnit lifecycle
|
|
12
|
+
* (before/beforeEach/afterEach/after hooks, assertion counting, steps tracking).
|
|
13
|
+
*
|
|
14
|
+
* @param {string} moduleName - Name of the test suite
|
|
15
|
+
* @param {object} [runtimeOptions] - Optional Deno BDD options forwarded to `describe()`
|
|
16
|
+
* (e.g. `{ concurrency: false }`, `{ permissions: { read: true } }`)
|
|
17
|
+
* @param {function} moduleContent - Callback that defines tests and hooks via `hooks.before`,
|
|
18
|
+
* `hooks.beforeEach`, `hooks.afterEach`, `hooks.after`
|
|
19
|
+
* @returns {void}
|
|
20
|
+
* @example
|
|
21
|
+
* ```js ignore
|
|
22
|
+
* import { module, test } from "qunitx";
|
|
23
|
+
*
|
|
24
|
+
* module("Math", (hooks) => {
|
|
25
|
+
* hooks.before((assert) => {
|
|
26
|
+
* assert.step("before hook ran");
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* test("addition", (assert) => {
|
|
30
|
+
* assert.equal(2 + 2, 4);
|
|
31
|
+
* });
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
8
35
|
export default function module(moduleName, runtimeOptions, moduleContent) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
36
|
+
const targetRuntimeOptions = moduleContent ? runtimeOptions : {};
|
|
37
|
+
const targetModuleContent = moduleContent ? moduleContent : runtimeOptions;
|
|
38
|
+
const moduleContext = new ModuleContext(moduleName);
|
|
12
39
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
40
|
+
describe(moduleName, { concurrency: true, ...targetRuntimeOptions }, function () {
|
|
41
|
+
const beforeHooks = [];
|
|
42
|
+
const afterHooks = [];
|
|
16
43
|
|
|
17
44
|
beforeAll(async function () {
|
|
18
45
|
Object.assign(moduleContext.context, moduleContext.moduleChain.reduce((result, module) => {
|
|
@@ -24,7 +51,7 @@ export default function module(moduleName, runtimeOptions, moduleContent) {
|
|
|
24
51
|
});
|
|
25
52
|
}, { steps: [], expectedAssertionCount: undefined }));
|
|
26
53
|
|
|
27
|
-
for (
|
|
54
|
+
for (const hook of beforeHooks) {
|
|
28
55
|
await hook.call(moduleContext.context, moduleContext.assert);
|
|
29
56
|
}
|
|
30
57
|
|
|
@@ -41,7 +68,7 @@ export default function module(moduleName, runtimeOptions, moduleContent) {
|
|
|
41
68
|
await assert.waitForAsyncOps();
|
|
42
69
|
}
|
|
43
70
|
|
|
44
|
-
|
|
71
|
+
const targetContext = moduleContext.tests[moduleContext.tests.length - 1];
|
|
45
72
|
for (let j = afterHooks.length - 1; j >= 0; j--) {
|
|
46
73
|
await afterHooks[j].call(targetContext, targetContext.assert);
|
|
47
74
|
}
|
package/shims/deno/test.js
CHANGED
|
@@ -2,29 +2,58 @@ import { it } from "jsr:@std/testing/bdd";
|
|
|
2
2
|
import TestContext from '../shared/test-context.js';
|
|
3
3
|
import ModuleContext from '../shared/module-context.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Defines an individual test within a module for Deno's BDD test runner.
|
|
7
|
+
*
|
|
8
|
+
* Wraps `it()` from `@std/testing/bdd` and handles the full QUnit lifecycle:
|
|
9
|
+
* beforeEach/afterEach hooks, async assertion waiting, and step verification.
|
|
10
|
+
*
|
|
11
|
+
* Must be called inside a `module()` callback.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} testName - Name of the test
|
|
14
|
+
* @param {object} [runtimeOptions] - Optional Deno BDD options forwarded to `it()`
|
|
15
|
+
* (e.g. `{ concurrency: false }`, `{ sanitizeExit: false }`)
|
|
16
|
+
* @param {function} testContent - Test callback receiving `(assert, { testName, options })`
|
|
17
|
+
* @returns {void}
|
|
18
|
+
* @example
|
|
19
|
+
* ```js ignore
|
|
20
|
+
* import { module, test } from "qunitx";
|
|
21
|
+
*
|
|
22
|
+
* module("Math", () => {
|
|
23
|
+
* test("addition", (assert) => {
|
|
24
|
+
* assert.equal(1 + 1, 2);
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* test("async resolves correctly", async (assert) => {
|
|
28
|
+
* const result = await Promise.resolve(42);
|
|
29
|
+
* assert.strictEqual(result, 42);
|
|
30
|
+
* });
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
5
34
|
export default function test(testName, runtimeOptions, testContent) {
|
|
6
|
-
|
|
35
|
+
const moduleContext = ModuleContext.lastModule;
|
|
7
36
|
if (!moduleContext) {
|
|
8
37
|
throw new Error(`Test '${testName}' called outside of module context.`);
|
|
9
38
|
}
|
|
10
39
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
40
|
+
const targetRuntimeOptions = testContent ? runtimeOptions : {};
|
|
41
|
+
const targetTestContent = testContent ? testContent : runtimeOptions;
|
|
42
|
+
const context = new TestContext(testName, moduleContext);
|
|
14
43
|
|
|
15
|
-
|
|
16
|
-
for (
|
|
17
|
-
for (
|
|
44
|
+
it(testName, { concurrency: true, ...targetRuntimeOptions }, async function () {
|
|
45
|
+
for (const module of context.module.moduleChain) {
|
|
46
|
+
for (const hook of module.beforeEachHooks) {
|
|
18
47
|
await hook.call(context, context.assert);
|
|
19
48
|
}
|
|
20
49
|
}
|
|
21
50
|
|
|
22
|
-
|
|
51
|
+
const result = await targetTestContent.call(context, context.assert, { testName, options: runtimeOptions });
|
|
23
52
|
|
|
24
53
|
await context.assert.waitForAsyncOps();
|
|
25
54
|
|
|
26
55
|
for (let i = context.module.moduleChain.length - 1; i >= 0; i--) {
|
|
27
|
-
|
|
56
|
+
const module = context.module.moduleChain[i];
|
|
28
57
|
for (let j = module.afterEachHooks.length - 1; j >= 0; j--) {
|
|
29
58
|
await module.afterEachHooks[j].call(context, context.assert);
|
|
30
59
|
}
|
package/shims/node/module.js
CHANGED
|
@@ -6,13 +6,13 @@ import ModuleContext from '../shared/module-context.js';
|
|
|
6
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
7
|
|
|
8
8
|
export default function module(moduleName, runtimeOptions, moduleContent) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const targetRuntimeOptions = moduleContent ? runtimeOptions : {};
|
|
10
|
+
const targetModuleContent = moduleContent ? moduleContent : runtimeOptions;
|
|
11
|
+
const moduleContext = new ModuleContext(moduleName);
|
|
12
12
|
|
|
13
|
-
return describe(moduleName, { concurrency: true, ...targetRuntimeOptions },
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
return describe(moduleName, { concurrency: true, ...targetRuntimeOptions }, function () {
|
|
14
|
+
const beforeHooks = [];
|
|
15
|
+
const afterHooks = [];
|
|
16
16
|
|
|
17
17
|
beforeAll(async function () {
|
|
18
18
|
Object.assign(moduleContext.context, moduleContext.moduleChain.reduce((result, module) => {
|
|
@@ -24,7 +24,7 @@ export default function module(moduleName, runtimeOptions, moduleContent) {
|
|
|
24
24
|
});
|
|
25
25
|
}, { steps: [], expectedAssertionCount: undefined }));
|
|
26
26
|
|
|
27
|
-
for (
|
|
27
|
+
for (const hook of beforeHooks) {
|
|
28
28
|
await hook.call(moduleContext.context, moduleContext.assert);
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -41,7 +41,7 @@ export default function module(moduleName, runtimeOptions, moduleContent) {
|
|
|
41
41
|
await assert.waitForAsyncOps();
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const targetContext = moduleContext.tests[moduleContext.tests.length - 1];
|
|
45
45
|
for (let j = afterHooks.length - 1; j >= 0; j--) {
|
|
46
46
|
await afterHooks[j].call(targetContext, targetContext.assert);
|
|
47
47
|
}
|
package/shims/node/test.js
CHANGED
|
@@ -3,28 +3,28 @@ import TestContext from '../shared/test-context.js';
|
|
|
3
3
|
import ModuleContext from '../shared/module-context.js';
|
|
4
4
|
|
|
5
5
|
export default function test(testName, runtimeOptions, testContent) {
|
|
6
|
-
|
|
6
|
+
const moduleContext = ModuleContext.lastModule;
|
|
7
7
|
if (!moduleContext) {
|
|
8
8
|
throw new Error(`Test '${testName}' called outside of module context.`);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const targetRuntimeOptions = testContent ? runtimeOptions : {};
|
|
12
|
+
const targetTestContent = testContent ? testContent : runtimeOptions;
|
|
13
|
+
const context = new TestContext(testName, moduleContext);
|
|
14
14
|
|
|
15
15
|
return it(testName, { concurrency: true, ...targetRuntimeOptions }, async function () {
|
|
16
|
-
for (
|
|
17
|
-
for (
|
|
16
|
+
for (const module of context.module.moduleChain) {
|
|
17
|
+
for (const hook of module.beforeEachHooks) {
|
|
18
18
|
await hook.call(context, context.assert);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
const result = await targetTestContent.call(context, context.assert, { testName, options: runtimeOptions });
|
|
23
23
|
|
|
24
24
|
await context.assert.waitForAsyncOps();
|
|
25
25
|
|
|
26
26
|
for (let i = context.module.moduleChain.length - 1; i >= 0; i--) {
|
|
27
|
-
|
|
27
|
+
const module = context.module.moduleChain[i];
|
|
28
28
|
for (let j = module.afterEachHooks.length - 1; j >= 0; j--) {
|
|
29
29
|
await module.afterEachHooks[j].call(context, context.assert);
|
|
30
30
|
}
|
package/shims/shared/assert.js
CHANGED
|
@@ -55,13 +55,13 @@ export default class Assert {
|
|
|
55
55
|
}
|
|
56
56
|
async() {
|
|
57
57
|
let resolveFn;
|
|
58
|
-
|
|
58
|
+
const done = new Promise(resolve => { resolveFn = resolve; });
|
|
59
59
|
|
|
60
60
|
this.test.asyncOps.push(done);
|
|
61
61
|
|
|
62
62
|
return () => { resolveFn(); };
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
waitForAsyncOps() {
|
|
65
65
|
return Promise.all(this.test.asyncOps);
|
|
66
66
|
}
|
|
67
67
|
pushResult(resultInfo = {}) {
|
|
@@ -147,8 +147,8 @@ export default class Assert {
|
|
|
147
147
|
}
|
|
148
148
|
propEqual(actual, expected, message) {
|
|
149
149
|
this._incrementAssertionCount();
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
const targetActual = objectValues(actual);
|
|
151
|
+
const targetExpected = objectValues(expected);
|
|
152
152
|
if (!Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
153
153
|
throw new Assert.AssertionError({
|
|
154
154
|
actual: targetActual,
|
|
@@ -160,8 +160,8 @@ export default class Assert {
|
|
|
160
160
|
}
|
|
161
161
|
notPropEqual(actual, expected, message) {
|
|
162
162
|
this._incrementAssertionCount();
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
const targetActual = objectValues(actual);
|
|
164
|
+
const targetExpected = objectValues(expected);
|
|
165
165
|
if (Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
166
166
|
throw new Assert.AssertionError({
|
|
167
167
|
actual: targetActual,
|
|
@@ -173,8 +173,8 @@ export default class Assert {
|
|
|
173
173
|
}
|
|
174
174
|
propContains(actual, expected, message) {
|
|
175
175
|
this._incrementAssertionCount();
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
const targetActual = objectValuesSubset(actual, expected);
|
|
177
|
+
const targetExpected = objectValues(expected, false);
|
|
178
178
|
if (!Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
179
179
|
throw new Assert.AssertionError({
|
|
180
180
|
actual: targetActual,
|
|
@@ -186,8 +186,8 @@ export default class Assert {
|
|
|
186
186
|
}
|
|
187
187
|
notPropContains(actual, expected, message) {
|
|
188
188
|
this._incrementAssertionCount();
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
const targetActual = objectValuesSubset(actual, expected);
|
|
190
|
+
const targetExpected = objectValues(expected);
|
|
191
191
|
if (Assert.QUnit.equiv(targetActual, targetExpected)) {
|
|
192
192
|
throw new Assert.AssertionError({
|
|
193
193
|
actual: targetActual,
|
|
@@ -247,7 +247,7 @@ export default class Assert {
|
|
|
247
247
|
}
|
|
248
248
|
throws(blockFn, expectedInput, assertionMessage) {
|
|
249
249
|
this?._incrementAssertionCount();
|
|
250
|
-
|
|
250
|
+
const [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
|
|
251
251
|
if (typeof blockFn !== 'function') {
|
|
252
252
|
throw new Assert.AssertionError({
|
|
253
253
|
actual: blockFn,
|
|
@@ -260,7 +260,7 @@ export default class Assert {
|
|
|
260
260
|
try {
|
|
261
261
|
blockFn();
|
|
262
262
|
} catch (error) {
|
|
263
|
-
|
|
263
|
+
const validation = validateException(error, expected, message);
|
|
264
264
|
if (validation.result === false) {
|
|
265
265
|
throw new Assert.AssertionError({
|
|
266
266
|
actual: validation.result,
|
|
@@ -282,8 +282,8 @@ export default class Assert {
|
|
|
282
282
|
}
|
|
283
283
|
async rejects(promise, expectedInput, assertionMessage) {
|
|
284
284
|
this._incrementAssertionCount();
|
|
285
|
-
|
|
286
|
-
|
|
285
|
+
const [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
|
|
286
|
+
const then = promise && promise.then;
|
|
287
287
|
if (typeof then !== 'function') {
|
|
288
288
|
throw new Assert.AssertionError({
|
|
289
289
|
actual: promise,
|
|
@@ -302,7 +302,7 @@ export default class Assert {
|
|
|
302
302
|
stackStartFn: this.rejects,
|
|
303
303
|
});
|
|
304
304
|
} catch (error) {
|
|
305
|
-
|
|
305
|
+
const validation = validateException(error, expected, message);
|
|
306
306
|
if (validation.result === false) {
|
|
307
307
|
throw new Assert.AssertionError({
|
|
308
308
|
actual: validation.result,
|
package/shims/shared/index.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
const hasOwn = Object.prototype.hasOwnProperty
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
|
|
7
|
-
return typeof obj;
|
|
8
|
-
} : function (obj) {
|
|
9
|
-
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
10
|
-
}, _typeof(obj);
|
|
11
|
-
}
|
|
3
|
+
const _typeof = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'
|
|
4
|
+
? (obj) => typeof obj
|
|
5
|
+
: (obj) => obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
|
|
12
6
|
|
|
13
7
|
export function objectType(obj) {
|
|
14
8
|
if (typeof obj === 'undefined') {
|
|
@@ -19,8 +13,8 @@ export function objectType(obj) {
|
|
|
19
13
|
if (obj === null) {
|
|
20
14
|
return 'null';
|
|
21
15
|
}
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
const match = toString.call(obj).match(/^\[object\s(.*)\]$/);
|
|
17
|
+
const type = match && match[1];
|
|
24
18
|
switch (type) {
|
|
25
19
|
case 'Number':
|
|
26
20
|
if (isNaN(obj)) {
|
|
@@ -47,12 +41,12 @@ function is(type, obj) {
|
|
|
47
41
|
}
|
|
48
42
|
|
|
49
43
|
export function objectValues(obj) {
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
const allowArray = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
45
|
+
const vals = allowArray && is('array', obj) ? [] : {};
|
|
52
46
|
|
|
53
|
-
for (
|
|
47
|
+
for (const key in obj) {
|
|
54
48
|
if (hasOwn.call(obj, key)) {
|
|
55
|
-
|
|
49
|
+
const val = obj[key];
|
|
56
50
|
vals[key] = val === Object(val) ? objectValues(val, allowArray) : val;
|
|
57
51
|
}
|
|
58
52
|
}
|
|
@@ -80,8 +74,8 @@ export function objectValuesSubset(obj, model) {
|
|
|
80
74
|
|
|
81
75
|
// Unlike objectValues(), subset arrays to a plain objects as well.
|
|
82
76
|
// This enables subsetting [20, 30] with {1: 30}.
|
|
83
|
-
|
|
84
|
-
for (
|
|
77
|
+
const subset = {};
|
|
78
|
+
for (const key in model) {
|
|
85
79
|
if (hasOwn.call(model, key) && hasOwn.call(obj, key)) {
|
|
86
80
|
subset[key] = objectValuesSubset(obj[key], model[key]);
|
|
87
81
|
}
|
|
@@ -90,7 +84,7 @@ export function objectValuesSubset(obj, model) {
|
|
|
90
84
|
}
|
|
91
85
|
|
|
92
86
|
export function validateExpectedExceptionArgs(expected, message, assertionMethod) {
|
|
93
|
-
|
|
87
|
+
const expectedType = objectType(expected);
|
|
94
88
|
|
|
95
89
|
// 'expected' is optional unless doing string comparison
|
|
96
90
|
if (expectedType === 'string') {
|
|
@@ -102,7 +96,7 @@ export function validateExpectedExceptionArgs(expected, message, assertionMethod
|
|
|
102
96
|
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.');
|
|
103
97
|
}
|
|
104
98
|
}
|
|
105
|
-
|
|
99
|
+
const valid = !expected ||
|
|
106
100
|
// TODO: be more explicit here
|
|
107
101
|
expectedType === 'regexp' || expectedType === 'function' || expectedType === 'object';
|
|
108
102
|
if (!valid) {
|
|
@@ -112,8 +106,8 @@ export function validateExpectedExceptionArgs(expected, message, assertionMethod
|
|
|
112
106
|
}
|
|
113
107
|
|
|
114
108
|
export function validateException(actual, expected, message) {
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
let result = false;
|
|
110
|
+
const expectedType = objectType(expected);
|
|
117
111
|
|
|
118
112
|
// These branches should be exhaustive, based on validation done in validateExpectedException
|
|
119
113
|
|
|
@@ -157,7 +151,7 @@ export function validateException(actual, expected, message) {
|
|
|
157
151
|
|
|
158
152
|
function errorString(error) {
|
|
159
153
|
// Use String() instead of toString() to handle non-object values like undefined or null.
|
|
160
|
-
|
|
154
|
+
const resultErrorString = String(error);
|
|
161
155
|
|
|
162
156
|
// If the error wasn't a subclass of Error but something like
|
|
163
157
|
// an object literal with name and message properties...
|
|
@@ -16,7 +16,7 @@ export default class ModuleContext {
|
|
|
16
16
|
tests = [];
|
|
17
17
|
|
|
18
18
|
constructor(name) {
|
|
19
|
-
|
|
19
|
+
const parentModule = ModuleContext.currentModuleChain[ModuleContext.currentModuleChain.length - 1];
|
|
20
20
|
|
|
21
21
|
ModuleContext.currentModuleChain.push(this);
|
|
22
22
|
|
package/Makefile
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
.PHONY: check test lint build release
|
|
2
|
-
|
|
3
|
-
check: lint test
|
|
4
|
-
|
|
5
|
-
lint:
|
|
6
|
-
npm run lint
|
|
7
|
-
|
|
8
|
-
test:
|
|
9
|
-
npm test
|
|
10
|
-
|
|
11
|
-
build:
|
|
12
|
-
npm run build
|
|
13
|
-
|
|
14
|
-
# Lint, bump version, update changelog, commit, tag, push, publish to npm.
|
|
15
|
-
# CI then creates the GitHub release.
|
|
16
|
-
# Usage: make release (defaults to patch)
|
|
17
|
-
# make release LEVEL=minor|major
|
|
18
|
-
LEVEL ?= patch
|
|
19
|
-
release:
|
|
20
|
-
@npm whoami 2>/dev/null || npm login
|
|
21
|
-
npm run lint
|
|
22
|
-
npm version $(LEVEL) --no-git-tag-version
|
|
23
|
-
npm run changelog:update
|
|
24
|
-
git add package.json package-lock.json CHANGELOG.md
|
|
25
|
-
git commit -m "Release $$(node -p 'require("./package.json").version')"
|
|
26
|
-
git tag "v$$(node -p 'require("./package.json").version')"
|
|
27
|
-
git push && git push --tags
|
|
28
|
-
npm publish --access public
|