@lumjs/tests 1.3.0 → 1.4.0
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/CHANGELOG.md +27 -1
- package/TODO.md +2 -0
- package/jsdoc.json +33 -0
- package/lib/functional.js +7 -1
- package/lib/harness.js +9 -0
- package/lib/index.js +17 -2
- package/lib/log.js +9 -5
- package/lib/test.js +269 -30
- package/package.json +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.4.0] - 2022-07-29
|
|
10
|
+
## Added
|
|
11
|
+
- Explicit `exports` section in `package.json` file.
|
|
12
|
+
- Added `test.done()` method to be used instead of `test.output()`.
|
|
13
|
+
- Added ability to configure the stringify depth.
|
|
14
|
+
- Added ability to detect if the script was ran directly.
|
|
15
|
+
- Added ability to check for a top-level `Harness` instance.
|
|
16
|
+
- Added four new *binary flag* comparitor tests to `cmp()` method.
|
|
17
|
+
- Added `not` alias for `!==` comparitor.
|
|
18
|
+
- Added `matches` method for using a regular expression to match a string.
|
|
19
|
+
- Added `callIs()` method that is like `call()` but takes a desired value and passes the function return value to `cmp()`, `isa()`, or other test methods.
|
|
20
|
+
- A new `test.ran` computed property.
|
|
21
|
+
|
|
22
|
+
## Changed
|
|
23
|
+
- Enhanced a lot of docblocks.
|
|
24
|
+
- Updated anything using `types.stringify()` to support the depth setting.
|
|
25
|
+
- Updated `run()` so it can use either `call()` or `callIs()` as the underlying test method when using a custom `function` test.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Configuration for JSDoc.
|
|
29
|
+
- A few module-level *docblocks*.
|
|
30
|
+
### Changed
|
|
31
|
+
- Updated `@lumjs/core` dependency to `^1.0.0` (no more *beta* tags!)
|
|
32
|
+
- Updated various *docblocks* for documentation.
|
|
33
|
+
|
|
9
34
|
## [1.3.0] - 2022-07-27
|
|
10
35
|
### Added
|
|
11
36
|
- `$call()` function; powers `call()`, `lives()`, `dies()`, and `diesWith()`.
|
|
@@ -57,7 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
57
82
|
- Ported from Lum.js v4 library set.
|
|
58
83
|
- Added a few more features from the PHP version.
|
|
59
84
|
|
|
60
|
-
[Unreleased]: https://github.com/supernovus/lum.tests.js/compare/v1.
|
|
85
|
+
[Unreleased]: https://github.com/supernovus/lum.tests.js/compare/v1.4.0...HEAD
|
|
86
|
+
[1.4.0]: https://github.com/supernovus/lum.tests.js/compare/v1.3.0...v1.4.0
|
|
61
87
|
[1.3.0]: https://github.com/supernovus/lum.tests.js/compare/v1.2.0...v1.3.0
|
|
62
88
|
[1.2.0]: https://github.com/supernovus/lum.tests.js/compare/v1.1.1...v1.2.0
|
|
63
89
|
[1.1.1]: https://github.com/supernovus/lum.tests.js/compare/v1.1.0...v1.1.1
|
package/TODO.md
CHANGED
package/jsdoc.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tags":
|
|
3
|
+
{
|
|
4
|
+
"allowUnknownTags": true
|
|
5
|
+
},
|
|
6
|
+
"source":
|
|
7
|
+
{
|
|
8
|
+
"include": ["./lib"]
|
|
9
|
+
},
|
|
10
|
+
"opts":
|
|
11
|
+
{
|
|
12
|
+
"destination": "./docs/api",
|
|
13
|
+
"recurse": true
|
|
14
|
+
},
|
|
15
|
+
"plugins":
|
|
16
|
+
[
|
|
17
|
+
"plugins/markdown"
|
|
18
|
+
],
|
|
19
|
+
"templates":
|
|
20
|
+
{
|
|
21
|
+
"cleverLinks": false,
|
|
22
|
+
"monospaceLinks": false,
|
|
23
|
+
"default":
|
|
24
|
+
{
|
|
25
|
+
"outputSourceFiles": true
|
|
26
|
+
},
|
|
27
|
+
"path": "ink-docstrap",
|
|
28
|
+
"theme": "cerulean",
|
|
29
|
+
"navType": "vertical",
|
|
30
|
+
"linenums": true,
|
|
31
|
+
"dateFormat": "YYYY-MM-DD, hh:mm:ss"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/lib/functional.js
CHANGED
|
@@ -4,6 +4,11 @@ const Test = require('./test');
|
|
|
4
4
|
// A list of methods we can proxy directly.
|
|
5
5
|
const PROXY_METHODS = Test.$METHODS.all;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Module defining the functional API.
|
|
9
|
+
* @module @lumjs/tests/functional
|
|
10
|
+
*/
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* A new test instance and a set of functions wrapping it.
|
|
9
14
|
* @typedef {object} Functional
|
|
@@ -21,11 +26,12 @@ const PROXY_METHODS = Test.$METHODS.all;
|
|
|
21
26
|
* Usage is like:
|
|
22
27
|
*
|
|
23
28
|
* ```js
|
|
24
|
-
* const {plan,ok,isa} = require('@lumjs/tests').functional({module});
|
|
29
|
+
* const {plan,ok,isa,done} = require('@lumjs/tests').functional({module});
|
|
25
30
|
*
|
|
26
31
|
* plan(2);
|
|
27
32
|
* ok(true, 'ok() works');
|
|
28
33
|
* isa(isa, 'function', 'isa is a function');
|
|
34
|
+
* done();
|
|
29
35
|
*
|
|
30
36
|
* ```
|
|
31
37
|
*
|
package/lib/harness.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
const Test = require('./test');
|
|
3
3
|
const Log = require('./log');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Module defining the Harness class.
|
|
7
|
+
* @module @lumjs/tests/harness
|
|
8
|
+
*/
|
|
9
|
+
|
|
5
10
|
/**
|
|
6
11
|
* A class that acts as a test harness for running other tests.
|
|
7
12
|
*
|
|
@@ -14,3 +19,7 @@ class Harness
|
|
|
14
19
|
throw new Error("Not yet implemented");
|
|
15
20
|
}
|
|
16
21
|
}
|
|
22
|
+
|
|
23
|
+
// Export it.
|
|
24
|
+
module.exports = Harness;
|
|
25
|
+
|
package/lib/index.js
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Several test related classes.
|
|
3
|
+
* @module @lumjs/tests
|
|
3
4
|
*/
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Test class.
|
|
8
|
+
* @alias module:@lumjs/tests.Test
|
|
9
|
+
* @see module:@lumjs/tests/test
|
|
10
|
+
*/
|
|
5
11
|
const Test = require('./test');
|
|
6
|
-
|
|
7
12
|
module.exports.Test = Test;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Test Harness class.
|
|
16
|
+
* @alias module:@lumjs/tests.Harness
|
|
17
|
+
* @see module:@lumjs/tests/harness
|
|
18
|
+
*/
|
|
8
19
|
module.exports.Harness = require('./harness');
|
|
9
20
|
|
|
10
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Functional API registration.
|
|
23
|
+
* @alias module:@lumjs/tests.functional
|
|
24
|
+
* @see module:@lumjs/tests/functional
|
|
25
|
+
*/
|
|
11
26
|
module.exports.functional = require('./functional');
|
|
12
27
|
|
|
13
28
|
/**
|
package/lib/log.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
const types = require('@lumjs/core').types;
|
|
3
|
-
const {
|
|
3
|
+
const {S,O,isArray,stringify,def} = types;
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A log representing the results of a test.
|
|
@@ -19,7 +19,7 @@ const {F,S,O,isArray,stringify} = types;
|
|
|
19
19
|
*/
|
|
20
20
|
class Log
|
|
21
21
|
{
|
|
22
|
-
constructor ()
|
|
22
|
+
constructor (test)
|
|
23
23
|
{
|
|
24
24
|
this.ok = false;
|
|
25
25
|
this.skipped = false;
|
|
@@ -27,6 +27,8 @@ class Log
|
|
|
27
27
|
this.desc = null;
|
|
28
28
|
this.directive = null;
|
|
29
29
|
this.details = {};
|
|
30
|
+
this.stringifyDepth = test.stringifyDepth;
|
|
31
|
+
def(this, '$test$', test);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -40,6 +42,8 @@ class Log
|
|
|
40
42
|
*/
|
|
41
43
|
tap (num)
|
|
42
44
|
{
|
|
45
|
+
const SD = this.stringifyDepth;
|
|
46
|
+
|
|
43
47
|
var out;
|
|
44
48
|
if (this.ok)
|
|
45
49
|
out = 'ok ';
|
|
@@ -66,8 +70,8 @@ class Log
|
|
|
66
70
|
var want = this.details.wanted;
|
|
67
71
|
if (this.details.stringify)
|
|
68
72
|
{
|
|
69
|
-
got = stringify(got);
|
|
70
|
-
want = stringify(want);
|
|
73
|
+
got = stringify(got, SD);
|
|
74
|
+
want = stringify(want, SD);
|
|
71
75
|
}
|
|
72
76
|
out += `# got: ${got}\n`;
|
|
73
77
|
out += `# expected: ${want}\n`;
|
|
@@ -86,7 +90,7 @@ class Log
|
|
|
86
90
|
|
|
87
91
|
for (const i in info)
|
|
88
92
|
{
|
|
89
|
-
const line = (typeof info[i] === S) ? info[i] : stringify(info[i]);
|
|
93
|
+
const line = (typeof info[i] === S) ? info[i] : stringify(info[i], SD);
|
|
90
94
|
out += `## ${line}\n`;
|
|
91
95
|
}
|
|
92
96
|
}
|
package/lib/test.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module defining the Test class.
|
|
3
|
+
* @module @lumjs/tests/test
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
const core = require('@lumjs/core');
|
|
2
7
|
const types = core.types;
|
|
3
|
-
const {F,S,N,isObj,isArray,def} = types;
|
|
8
|
+
const {F,S,N,isObj,isArray,needs,def} = types;
|
|
4
9
|
|
|
5
10
|
// We use a separate class to represent test logs.
|
|
6
11
|
const Log = require('./log');
|
|
@@ -15,7 +20,7 @@ const TEST_METHODS =
|
|
|
15
20
|
// A list of other methods to export that are not standard tests.
|
|
16
21
|
const META_METHODS =
|
|
17
22
|
[
|
|
18
|
-
'plan', 'diag', 'run', 'tap', 'output',
|
|
23
|
+
'plan', 'diag', 'run', 'tap', 'output', 'done',
|
|
19
24
|
];
|
|
20
25
|
|
|
21
26
|
// The function that powers `Test.call()` and friends.
|
|
@@ -39,6 +44,14 @@ function $call (testfunc, args)
|
|
|
39
44
|
* Based on Lum.php's Test library.
|
|
40
45
|
* Which itself was based on Perl 5's Test::More, and
|
|
41
46
|
* Raku's Test libraries.
|
|
47
|
+
*
|
|
48
|
+
* @property {number} planned - Number of tests planned, `0` if unplanned.
|
|
49
|
+
* @property {number} failed - Number of tests that failed.
|
|
50
|
+
* @property {number} skipped - Number of tests that were skipped.
|
|
51
|
+
* @property {number} ran - Number of tests ran (*calculated*).
|
|
52
|
+
* @property {string} id - Unique test id used by `Harness` libary.
|
|
53
|
+
* @property {boolean} isTop - Test module was loaded from the command line.
|
|
54
|
+
* @property {?object} harness - The top-level `Harness` if one was found.
|
|
42
55
|
*/
|
|
43
56
|
class Test
|
|
44
57
|
{
|
|
@@ -58,6 +71,9 @@ class Test
|
|
|
58
71
|
* Also, if this is passed, and `opts.id` was not specified, and id
|
|
59
72
|
* will be auto-generated based on the filename of the module.
|
|
60
73
|
*
|
|
74
|
+
* @param {number} [opts.stringify=1] The depth `stringify()` should recurse
|
|
75
|
+
* objects and Arrays before switching to plain JSON stringification.
|
|
76
|
+
*
|
|
61
77
|
*/
|
|
62
78
|
constructor (opts={})
|
|
63
79
|
{
|
|
@@ -85,20 +101,46 @@ class Test
|
|
|
85
101
|
this.id = null;
|
|
86
102
|
}
|
|
87
103
|
|
|
104
|
+
this.stringifyDepth = opts.stringify ?? 1;
|
|
105
|
+
|
|
88
106
|
this.failed = 0;
|
|
89
107
|
this.skipped = 0;
|
|
90
108
|
this.planned = 0;
|
|
91
109
|
this.log = [];
|
|
92
110
|
|
|
111
|
+
// These three will be updated below if possible.
|
|
112
|
+
this.isTop = false;
|
|
113
|
+
this.harness = null;
|
|
114
|
+
|
|
93
115
|
if (typeof opts.plan === N)
|
|
94
116
|
{
|
|
95
117
|
this.plan(opts.plan);
|
|
96
118
|
}
|
|
97
119
|
|
|
98
120
|
if (hasModule)
|
|
99
|
-
{ //
|
|
121
|
+
{ // If a module was passed, its going to export this test.
|
|
100
122
|
opts.module.exports = this;
|
|
123
|
+
// We'll also use the module to determine if we're Harnessed or not.
|
|
124
|
+
if (require.main === opts.module)
|
|
125
|
+
{ // Was called directly.
|
|
126
|
+
this.isTop = true;
|
|
127
|
+
}
|
|
101
128
|
}
|
|
129
|
+
|
|
130
|
+
if (!this.isTop)
|
|
131
|
+
{ // Try to find a Harness instance.
|
|
132
|
+
if (isObj(require.main) && require.main.exports instanceof Harness)
|
|
133
|
+
{ // We found the Harness instance.
|
|
134
|
+
this.harness = require.main.exports;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// A wrapper around types.stringify()
|
|
141
|
+
stringify(what)
|
|
142
|
+
{
|
|
143
|
+
return types.stringify(what, this.stringifyDepth);
|
|
102
144
|
}
|
|
103
145
|
|
|
104
146
|
/**
|
|
@@ -142,7 +184,7 @@ class Test
|
|
|
142
184
|
*/
|
|
143
185
|
ok (test, desc, directive, details)
|
|
144
186
|
{
|
|
145
|
-
const log = new Log();
|
|
187
|
+
const log = new Log(this);
|
|
146
188
|
|
|
147
189
|
if (test)
|
|
148
190
|
{
|
|
@@ -185,7 +227,7 @@ class Test
|
|
|
185
227
|
* @param {...any} [args] Arguments to pass to the test function.
|
|
186
228
|
* @returns {Log}
|
|
187
229
|
*/
|
|
188
|
-
call(testfunc, desc, ...args)
|
|
230
|
+
call (testfunc, desc, ...args)
|
|
189
231
|
{
|
|
190
232
|
const ret = $call(testfunc, args);
|
|
191
233
|
return this.ok(ret.val, desc, ret.err);
|
|
@@ -257,7 +299,7 @@ class Test
|
|
|
257
299
|
* @param {...any} [args]
|
|
258
300
|
* @returns {Log}
|
|
259
301
|
*/
|
|
260
|
-
diesWith(testfunc, testerr, desc, ...args)
|
|
302
|
+
diesWith (testfunc, testerr, desc, ...args)
|
|
261
303
|
{
|
|
262
304
|
let ok = false, details = {}, err = null;
|
|
263
305
|
|
|
@@ -320,15 +362,22 @@ class Test
|
|
|
320
362
|
* @param {string} comp - A comparitor to test with.
|
|
321
363
|
*
|
|
322
364
|
* - `===`, `is` (See also: `is()`)
|
|
323
|
-
* - `!==`, `isnt`
|
|
365
|
+
* - `!==`, `isnt`, `not` (See also: `isnt()`)
|
|
324
366
|
* - `==`, `eq`
|
|
325
367
|
* - `!=`, `ne`
|
|
326
368
|
* - `>`, `gt`
|
|
327
369
|
* - `<`, `lt`
|
|
328
370
|
* - `>=`, `ge`, `gte`
|
|
329
371
|
* - `<=`, `le`, `lte`
|
|
372
|
+
*
|
|
373
|
+
* A few special comparitors for *binary flag* testing:
|
|
374
|
+
*
|
|
375
|
+
* - `=&` → `((got & want) === want)`
|
|
376
|
+
* - `!&` → `((got & want) !== want)`
|
|
377
|
+
* - `+&` → `((got & want) !== 0)`
|
|
378
|
+
* - `-&` → `((got & want) === 0)`
|
|
330
379
|
*
|
|
331
|
-
* @param {string} desc
|
|
380
|
+
* @param {string} [desc]
|
|
332
381
|
* @param {boolean} [stringify=true] Stringify values in TAP output?
|
|
333
382
|
* @returns {Log}
|
|
334
383
|
*/
|
|
@@ -343,6 +392,7 @@ class Test
|
|
|
343
392
|
break;
|
|
344
393
|
case 'isnt':
|
|
345
394
|
case '!==':
|
|
395
|
+
case 'not':
|
|
346
396
|
test = (got !== want);
|
|
347
397
|
break;
|
|
348
398
|
case 'eq':
|
|
@@ -371,6 +421,16 @@ class Test
|
|
|
371
421
|
case '>=':
|
|
372
422
|
test = (got >= want);
|
|
373
423
|
break;
|
|
424
|
+
case '=&':
|
|
425
|
+
test = ((got&want)===want);
|
|
426
|
+
break;
|
|
427
|
+
case '!&':
|
|
428
|
+
test = ((got&want)!==want)
|
|
429
|
+
case '+&':
|
|
430
|
+
test = ((got&want)!==0);
|
|
431
|
+
break;
|
|
432
|
+
case '-&':
|
|
433
|
+
test = ((got&want)===0);
|
|
374
434
|
default:
|
|
375
435
|
test = false;
|
|
376
436
|
}
|
|
@@ -390,6 +450,39 @@ class Test
|
|
|
390
450
|
return this.ok(test, desc, null, details);
|
|
391
451
|
}
|
|
392
452
|
|
|
453
|
+
/**
|
|
454
|
+
* See if a string matches a value.
|
|
455
|
+
*
|
|
456
|
+
* @param {string} got
|
|
457
|
+
* @param {RegExp} want
|
|
458
|
+
* @param {string} [desc]
|
|
459
|
+
* @param {boolean} [stringify=true]
|
|
460
|
+
* @returns {Log}
|
|
461
|
+
*/
|
|
462
|
+
matches(got, want, desc, stringify=true)
|
|
463
|
+
{
|
|
464
|
+
const no = {error: "matches 'got' value must be a string"};
|
|
465
|
+
needs(got, no, S);
|
|
466
|
+
no.error = "matches 'want' value must be a RegExp";
|
|
467
|
+
needs(want, no, RegExp);
|
|
468
|
+
|
|
469
|
+
const test = want.test(got);
|
|
470
|
+
|
|
471
|
+
let details = null;
|
|
472
|
+
if (!test)
|
|
473
|
+
{ // The test failed, add the deets.
|
|
474
|
+
details =
|
|
475
|
+
{
|
|
476
|
+
got,
|
|
477
|
+
wanted: want,
|
|
478
|
+
stringify,
|
|
479
|
+
comparitor: 'matches',
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return this.ok(test, desc, null, details);
|
|
484
|
+
}
|
|
485
|
+
|
|
393
486
|
/**
|
|
394
487
|
* See if two values are equal.
|
|
395
488
|
*
|
|
@@ -486,13 +579,13 @@ class Test
|
|
|
486
579
|
*
|
|
487
580
|
* @param {*} got
|
|
488
581
|
* @param {*} want
|
|
489
|
-
* @param {string} desc
|
|
490
|
-
* @returns
|
|
582
|
+
* @param {string} [desc]
|
|
583
|
+
* @returns {Log}
|
|
491
584
|
*/
|
|
492
585
|
isJSON (got, want, desc)
|
|
493
586
|
{
|
|
494
|
-
got =
|
|
495
|
-
want =
|
|
587
|
+
got = this.stringify(got);
|
|
588
|
+
want = this.stringify(want);
|
|
496
589
|
return this.is(got, want, desc, false);
|
|
497
590
|
}
|
|
498
591
|
|
|
@@ -503,16 +596,99 @@ class Test
|
|
|
503
596
|
*
|
|
504
597
|
* @param {*} got
|
|
505
598
|
* @param {*} want
|
|
506
|
-
* @param {string} desc
|
|
507
|
-
* @returns
|
|
599
|
+
* @param {string} [desc]
|
|
600
|
+
* @returns {Log}
|
|
508
601
|
*/
|
|
509
602
|
isntJSON (got, want, desc)
|
|
510
603
|
{
|
|
511
|
-
got =
|
|
512
|
-
want =
|
|
604
|
+
got = this.stringify(got);
|
|
605
|
+
want = this.stringify(want);
|
|
513
606
|
return this.isnt(got, want, desc, false);
|
|
514
607
|
}
|
|
515
608
|
|
|
609
|
+
/**
|
|
610
|
+
* Run a function and see if it's return value is what we wanted.
|
|
611
|
+
*
|
|
612
|
+
* @param {function} testfunc - The function to run.
|
|
613
|
+
* The return value will be passed to `cmp()` or another appropriate
|
|
614
|
+
* testing method as determined by the options.
|
|
615
|
+
* How this handles error handling is determined by options as well.
|
|
616
|
+
*
|
|
617
|
+
* @param {*} want - The value we want.
|
|
618
|
+
* @param {(object|string)} [opts] Named options for further behaviour.
|
|
619
|
+
* If it is a string it's considered the `opts.desc` option.
|
|
620
|
+
* @param {string} [opts.desc] A description for `ok()`.
|
|
621
|
+
* @param {boolean} [opts.stringify=true]
|
|
622
|
+
* @param {Array} [opts.args] Arguments to pass to the test function.
|
|
623
|
+
* @param {string} [opts.comp="is"] - The comparitor to test with.
|
|
624
|
+
* In addition to all of the comparitors from `cmp()`, there are a few
|
|
625
|
+
* extra comparitors that will pass through to other methods:
|
|
626
|
+
* - `isa` → Use `isa()` to test return value.
|
|
627
|
+
* - `nota` → Use `nota()` to test return value.
|
|
628
|
+
* - `=json`, `isJSON` → Use `isJSON()` to test return value.
|
|
629
|
+
* - `!json`, `isntJSON` → Use `isntJSON()` to test return value.
|
|
630
|
+
* - `matches` → Use `matches()` to test return value.
|
|
631
|
+
*
|
|
632
|
+
* @param {boolean} [opts.thrown=false] How to handle thrown errors.
|
|
633
|
+
*
|
|
634
|
+
* If this is `true`, then anything thrown will be passed as if it was
|
|
635
|
+
* the return value from the function.
|
|
636
|
+
*
|
|
637
|
+
* If this is `false`, then any errors thrown will result in an immediate
|
|
638
|
+
* failure of the test without any further processing, and the error will
|
|
639
|
+
* be passed as the `directive` to the `ok()` method.
|
|
640
|
+
*
|
|
641
|
+
* @returns {Log}
|
|
642
|
+
*/
|
|
643
|
+
callIs (testfunc, want, opts={})
|
|
644
|
+
{
|
|
645
|
+
const args = opts.args ?? [];
|
|
646
|
+
const ret = $call(testfunc, args);
|
|
647
|
+
const desc = opts.desc;
|
|
648
|
+
|
|
649
|
+
let got;
|
|
650
|
+
|
|
651
|
+
if (ret.err)
|
|
652
|
+
{ // How to handle errors.
|
|
653
|
+
if (opts.thrown)
|
|
654
|
+
{ // We're going to test the error.
|
|
655
|
+
got = ret.err;
|
|
656
|
+
}
|
|
657
|
+
else
|
|
658
|
+
{ // This is an automatic failure.
|
|
659
|
+
return this.ok(false, desc, ret.err);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
else
|
|
663
|
+
{ // No errors, good, testing against the return value.
|
|
664
|
+
got = ret.val;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const CFUN =
|
|
668
|
+
{
|
|
669
|
+
'matches': 'matches',
|
|
670
|
+
'isa': 'isa',
|
|
671
|
+
'nota': 'nota',
|
|
672
|
+
'isJSON': 'isJSON',
|
|
673
|
+
'=json': 'isJSON',
|
|
674
|
+
'isntJSON': 'isntJSON',
|
|
675
|
+
'!json': 'isntJSON',
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
const comp = opts.comp ?? 'is';
|
|
679
|
+
const stringify = opts.stringify ?? true;
|
|
680
|
+
|
|
681
|
+
if (typeof CFUN[comp] === S)
|
|
682
|
+
{ // A function with a custom return value.
|
|
683
|
+
const meth = CFUN[comp];
|
|
684
|
+
return this[meth](got, want, desc, stringify);
|
|
685
|
+
}
|
|
686
|
+
else
|
|
687
|
+
{ // We're going to use the cmp() method.
|
|
688
|
+
return this.cmp(got, want, comp, desc, stringify);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
516
692
|
/**
|
|
517
693
|
* Skip a test.
|
|
518
694
|
*
|
|
@@ -522,7 +698,7 @@ class Test
|
|
|
522
698
|
*/
|
|
523
699
|
skip (reason, desc)
|
|
524
700
|
{
|
|
525
|
-
|
|
701
|
+
const log = this.ok(true, desc);
|
|
526
702
|
log.skipped = true;
|
|
527
703
|
if (typeof reason === S)
|
|
528
704
|
log.skippedReason = reason;
|
|
@@ -554,35 +730,55 @@ class Test
|
|
|
554
730
|
* methods in this class, it will be set as the *current* test method.
|
|
555
731
|
*
|
|
556
732
|
* If this is a `function`, it will be set as the *current* test method.
|
|
557
|
-
*
|
|
733
|
+
* By default function test methods are passed to `call()` with the test
|
|
734
|
+
* parameters. However, if the *previous* test method was `callIs` then
|
|
735
|
+
* the `callIs()` method will be used as long as the custom function is
|
|
736
|
+
* the *current* test method. Likewise to switch back to `call()` simply
|
|
737
|
+
* set the *current* test method to `call` before setting it to a new custom
|
|
738
|
+
* test `function`.
|
|
558
739
|
*
|
|
559
740
|
* If this is an `Array` then it's the parameters for the *current* test
|
|
560
|
-
* method. If a custom `function` is in use, remember that the
|
|
561
|
-
*
|
|
562
|
-
*
|
|
741
|
+
* method. If a custom `function` is in use, remember that it's the
|
|
742
|
+
* `call()` or `callIs()` methods that will be being called, with their
|
|
743
|
+
* first parameter always being the custom function.
|
|
563
744
|
*
|
|
564
745
|
* Any value other than one of those will throw a `TypeError`.
|
|
565
746
|
*
|
|
566
747
|
* @returns {Log[]} A `Log` item for each test that was ran.
|
|
567
748
|
*/
|
|
568
|
-
run(...tests)
|
|
749
|
+
run (...tests)
|
|
569
750
|
{
|
|
751
|
+
const CF = 'call';
|
|
752
|
+
const CI = 'callIs';
|
|
753
|
+
|
|
570
754
|
const logs = [];
|
|
755
|
+
let funcall = CF;
|
|
571
756
|
let current = 'ok';
|
|
572
757
|
|
|
573
758
|
for (const test of tests)
|
|
574
759
|
{
|
|
575
760
|
const tt = typeof test;
|
|
576
|
-
if (tt ===
|
|
577
|
-
{ // Set the current test.
|
|
761
|
+
if (tt === S && TEST_METHODS.includes(test))
|
|
762
|
+
{ // Set the current test to a built-in.
|
|
578
763
|
current = test;
|
|
579
764
|
}
|
|
765
|
+
else if (tt === F)
|
|
766
|
+
{ // A custom test function for further tests.
|
|
767
|
+
if (current === CI)
|
|
768
|
+
{ // Last test was `callIs` using that for the custom function.
|
|
769
|
+
funcall = CI;
|
|
770
|
+
}
|
|
771
|
+
else if (current === CF)
|
|
772
|
+
{ // Last test was `call`, using that for the custom function.
|
|
773
|
+
funcall = CF;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
580
776
|
else if (isArray(test))
|
|
581
777
|
{ // A set of test parameters.
|
|
582
778
|
let log;
|
|
583
779
|
if (typeof current === F)
|
|
584
780
|
{ // A custom test function is in use.
|
|
585
|
-
log = this
|
|
781
|
+
log = this[funcall](current, ...test);
|
|
586
782
|
}
|
|
587
783
|
else
|
|
588
784
|
{ // A standard test is in use.
|
|
@@ -602,15 +798,14 @@ class Test
|
|
|
602
798
|
*/
|
|
603
799
|
tap ()
|
|
604
800
|
{
|
|
605
|
-
|
|
801
|
+
let out = '';
|
|
606
802
|
if (this.planned > 0)
|
|
607
803
|
{
|
|
608
804
|
out += '1..'+this.planned+"\n";
|
|
609
805
|
}
|
|
610
|
-
|
|
611
|
-
for (
|
|
806
|
+
let t = 1;
|
|
807
|
+
for (const log of this.log)
|
|
612
808
|
{
|
|
613
|
-
var log = this.log[i];
|
|
614
809
|
if (log instanceof Log)
|
|
615
810
|
{
|
|
616
811
|
out += log.tap(t++);
|
|
@@ -631,7 +826,7 @@ class Test
|
|
|
631
826
|
out += ' out of '+this.planned;
|
|
632
827
|
out += "\n";
|
|
633
828
|
}
|
|
634
|
-
|
|
829
|
+
const ran = t-1;
|
|
635
830
|
if (this.planned > 0 && this.planned != ran)
|
|
636
831
|
{
|
|
637
832
|
out += '# Looks like you planned '+this.planned+' but ran '+ran+" tests\n";
|
|
@@ -639,8 +834,28 @@ class Test
|
|
|
639
834
|
return out;
|
|
640
835
|
}
|
|
641
836
|
|
|
837
|
+
/**
|
|
838
|
+
* A calculated property of the number of tests that were ran.
|
|
839
|
+
* @type {int}
|
|
840
|
+
*/
|
|
841
|
+
get ran ()
|
|
842
|
+
{
|
|
843
|
+
let ran = 0;
|
|
844
|
+
for (const log of this.log)
|
|
845
|
+
{
|
|
846
|
+
if (log instanceof Log)
|
|
847
|
+
{
|
|
848
|
+
ran++;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return ran;
|
|
852
|
+
}
|
|
853
|
+
|
|
642
854
|
/**
|
|
643
855
|
* Send the TAP output to the `console`.
|
|
856
|
+
*
|
|
857
|
+
* This is a low-level method and is no longer recommended for use.
|
|
858
|
+
* Instead call the `done()` method, which will *do the right thing*.
|
|
644
859
|
*/
|
|
645
860
|
output ()
|
|
646
861
|
{
|
|
@@ -648,6 +863,25 @@ class Test
|
|
|
648
863
|
return this;
|
|
649
864
|
}
|
|
650
865
|
|
|
866
|
+
/**
|
|
867
|
+
* We're done testing.
|
|
868
|
+
*
|
|
869
|
+
* This will mark the test-set as finished, so attempting to run further
|
|
870
|
+
* tests after will result in a `RangeError` being thrown.
|
|
871
|
+
*
|
|
872
|
+
* If no `Harness` is in use, this will also run `this.output()`.
|
|
873
|
+
*/
|
|
874
|
+
done ()
|
|
875
|
+
{
|
|
876
|
+
if (this.$done)
|
|
877
|
+
{
|
|
878
|
+
throw new RangeError('Test set is already done');
|
|
879
|
+
}
|
|
880
|
+
this.$done = true;
|
|
881
|
+
|
|
882
|
+
return (this.harness ? this : this.output());
|
|
883
|
+
}
|
|
884
|
+
|
|
651
885
|
} // class Test
|
|
652
886
|
|
|
653
887
|
// Should never need this, but...
|
|
@@ -679,3 +913,8 @@ def(Test, '$METHODS',
|
|
|
679
913
|
|
|
680
914
|
// Export the class
|
|
681
915
|
module.exports = Test;
|
|
916
|
+
|
|
917
|
+
// Finally at the bottom after `module.exports` has been set, we will load
|
|
918
|
+
// the Harness class to avoid circular references failing.
|
|
919
|
+
const Harness = require('./harness');
|
|
920
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumjs/tests",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
|
+
"exports":
|
|
6
|
+
{
|
|
7
|
+
".": "./lib/index.js",
|
|
8
|
+
"./test": "./lib/test.js",
|
|
9
|
+
"./functional": "./lib/functional.js",
|
|
10
|
+
"./harness": "./lib/harness.js",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
5
13
|
"license": "MIT",
|
|
6
14
|
"repository":
|
|
7
15
|
{
|
|
@@ -9,11 +17,12 @@
|
|
|
9
17
|
"url": "https://github.com/supernovus/lum.tests.js.git"
|
|
10
18
|
},
|
|
11
19
|
"dependencies": {
|
|
12
|
-
"@lumjs/core": "^1.
|
|
20
|
+
"@lumjs/core": "^1.1.0"
|
|
13
21
|
},
|
|
14
22
|
"scripts":
|
|
15
23
|
{
|
|
16
24
|
"-TODO-1": "When Harness and bin/lumtest are done, use that for 'test'",
|
|
17
|
-
"test": "prove -e node --ext js ./test"
|
|
25
|
+
"test": "prove -e node --ext js ./test",
|
|
26
|
+
"build-docs": "jsdoc -c ./jsdoc.json"
|
|
18
27
|
}
|
|
19
28
|
}
|