@lumjs/tests 1.8.2 → 2.0.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 +48 -1
- package/README.md +0 -9
- package/TODO.md +1 -2
- package/bin/lumtest.js +6 -4
- package/lib/harness/index.js +81 -53
- package/lib/harness/parser.js +4 -4
- package/lib/harness/{plugin.js → plugin/index.js} +11 -22
- package/lib/harness/{node.js → plugin/node.js} +3 -3
- package/lib/test/functional.js +14 -3
- package/lib/test/index.js +9 -8
- package/lib/test/log.js +2 -2
- package/lib/test/stats.js +122 -23
- package/package.json +5 -2
- package/lib/harness/browser.js +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,51 @@ and a reference to a property of a module will be in `@tests.propName` format.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [2.0.0] - 2024-08-14
|
|
15
|
+
### Added
|
|
16
|
+
- The `Stats` class (and `Test` which extends it) now has async support.
|
|
17
|
+
- A new `.waiting` integer property indicates if async calls are in use.
|
|
18
|
+
- New `opts.async` nested options added to constructor options.
|
|
19
|
+
- The `async()` method takes an async function that returns a Promise,
|
|
20
|
+
increments the `.waiting` property, and then decrements it once the
|
|
21
|
+
returned Promise is either resolved or rejected.
|
|
22
|
+
- A `wait()` method is used by the `done()` method, and the Harness
|
|
23
|
+
to wait for all async calls to finish.
|
|
24
|
+
- A new `registerGlobal` option for the `Harness` constructor that if `true`
|
|
25
|
+
will make it assign the instance to a special global variable.
|
|
26
|
+
### Changed
|
|
27
|
+
- The `Harness` constructor now requires a `Plugin` as a mandatory
|
|
28
|
+
parameter. No more auto-detection or other crufty magic.
|
|
29
|
+
- The `bin/lumtest.js` script was updated with the API changes.
|
|
30
|
+
- The `Plugin` constructor no longer takes the Harness instance as
|
|
31
|
+
a paramter. The `harness` property will be assigned directly by
|
|
32
|
+
the harness instance instead.
|
|
33
|
+
- `Plugin` class no longer extends the deprecated `core.AbstractClass`.
|
|
34
|
+
- `Harness` has support for the new async functionality.
|
|
35
|
+
- The `run()` method is now explicitly `async`, as are its sub-methods.
|
|
36
|
+
- Moved Harness Plugins into a `harness/plugin` sub-folder.
|
|
37
|
+
- `Stats` can find the harness via the `registerGlobal` feature.
|
|
38
|
+
It still supports the CommonJS `require.main` method, but no
|
|
39
|
+
longer depends on it.
|
|
40
|
+
- The `functional` module now supports using a different name
|
|
41
|
+
for the exported function than the underlying method. Useful
|
|
42
|
+
as the `async()` method is a reserved word and cannot be used
|
|
43
|
+
as a variable/function name (so it's renamed to `callAsync()`).
|
|
44
|
+
- A bunch of extra modules are exported directly now.
|
|
45
|
+
- Renamed some constructors for easier debugging.
|
|
46
|
+
### Removed
|
|
47
|
+
- The `harness/browser` plugin that I never implemented.
|
|
48
|
+
That's better done in a separate package.
|
|
49
|
+
|
|
50
|
+
## [1.9.0] - 2024-07-27
|
|
51
|
+
### Changed
|
|
52
|
+
- Renamed `Harness` constructor name to `LumTestsHarness` to be more unique.
|
|
53
|
+
- The `Stats` class no longer requires the `../harness` module, but instead
|
|
54
|
+
checks if the object instance's constructor name is `LumTestsHarness`.
|
|
55
|
+
This will allow the `test` and `test/functional` modules to be bundled
|
|
56
|
+
by Webpack without bundling `harness` which currently has some issues.
|
|
57
|
+
- Minor bug fixes from `1.8.1` and `1.8.2` unlisted releases.
|
|
58
|
+
|
|
14
59
|
## [1.8.0] - 2023-01-06
|
|
15
60
|
### Changed
|
|
16
61
|
- Bumped `@lumjs/core` to `1.8.0`.
|
|
@@ -140,7 +185,9 @@ and a reference to a property of a module will be in `@tests.propName` format.
|
|
|
140
185
|
- Ported from Lum.js v4 library set.
|
|
141
186
|
- Added a few more features from the PHP version.
|
|
142
187
|
|
|
143
|
-
[Unreleased]: https://github.com/supernovus/lum.tests.js/compare/
|
|
188
|
+
[Unreleased]: https://github.com/supernovus/lum.tests.js/compare/v2.0.0...HEAD
|
|
189
|
+
[2.0.0]: https://github.com/supernovus/lum.tests.js/compare/v1.9.0...v2.0.0
|
|
190
|
+
[1.9.0]: https://github.com/supernovus/lum.tests.js/compare/v1.8.0...v1.9.0
|
|
144
191
|
[1.8.0]: https://github.com/supernovus/lum.tests.js/compare/v1.7.1...v1.8.0
|
|
145
192
|
[1.7.1]: https://github.com/supernovus/lum.tests.js/compare/v1.7.0...v1.7.1
|
|
146
193
|
[1.7.0]: https://github.com/supernovus/lum.tests.js/compare/v1.6.0...v1.7.0
|
package/README.md
CHANGED
|
@@ -6,15 +6,6 @@ This is nowhere near as advanced as many of the other options out there,
|
|
|
6
6
|
but does what I need it to do, and works with the TAP testing protocol that
|
|
7
7
|
the Perl and Raku programming languages popularized.
|
|
8
8
|
|
|
9
|
-
## Exports
|
|
10
|
-
|
|
11
|
-
| Name | Description |
|
|
12
|
-
| -------------------- | ---------------------------------------------------- |
|
|
13
|
-
| `Test` | The core class for testing. |
|
|
14
|
-
| `Harness` | A class for running a bunch of tests together. |
|
|
15
|
-
| `functional()` | A function for using functional-style testing. |
|
|
16
|
-
| `new()` | A function for getting a new `Test` instance. |
|
|
17
|
-
|
|
18
9
|
## Official URLs
|
|
19
10
|
|
|
20
11
|
This library can be found in two places:
|
package/TODO.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# TODO
|
|
2
2
|
|
|
3
|
-
- Update to properly support `async` tests; the `harness` will need some work.
|
|
4
3
|
- Write tests for:
|
|
5
4
|
- `call()`
|
|
6
5
|
- `diesWith()`
|
|
7
6
|
- `callIs()`
|
|
8
7
|
- `matches()`
|
|
9
8
|
- `run()`
|
|
9
|
+
- `async()` (currently used in `@lumjs/encode:v2.0.0` package).
|
|
10
10
|
- Test the `harness/parser` and associated `grammar/tap` libraries.
|
|
11
11
|
- Test the *external* test mode in `Harness`.
|
|
12
|
-
|
package/bin/lumtest.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const process = require('node:process');
|
|
3
3
|
const Harness = require('../lib/harness');
|
|
4
|
+
const NodePlugin = require('../lib/harness/plugin/node');
|
|
4
5
|
const core = require('@lumjs/core');
|
|
5
6
|
const {S} = core.types;
|
|
6
7
|
|
|
@@ -59,7 +60,8 @@ for (const arg of args)
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
harness.
|
|
63
|
+
// Create our Harness instance.
|
|
64
|
+
const harness = new Harness(NodePlugin, hOpts);
|
|
65
|
+
module.exports = harness; // Export it as the 'main' module.
|
|
66
|
+
harness.addDir(aDir, aOpts); // Add all the files in the test dir.
|
|
67
|
+
harness.run(rPlan); // Run all the tests.
|
package/lib/harness/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const core = require('@lumjs/core');
|
|
2
|
-
const {def,lazy
|
|
2
|
+
const {def,lazy} = core;
|
|
3
3
|
const {B,F} = core.types;
|
|
4
4
|
const QueuedTest = require('./queuedtest');
|
|
5
5
|
const Plugin = require('./plugin');
|
|
@@ -12,16 +12,13 @@ const Plugin = require('./plugin');
|
|
|
12
12
|
*
|
|
13
13
|
* @prop {object} opts - Options passed to constructor
|
|
14
14
|
*
|
|
15
|
-
* @prop {
|
|
15
|
+
* @prop {module:@lumjs/tests/test/stats} testSuite
|
|
16
16
|
*
|
|
17
17
|
* This meta-test will keep track of the success or failure of
|
|
18
18
|
* all the other tests. So its `plan` will be equal to the number of
|
|
19
19
|
* tests we're running, and so forth.
|
|
20
20
|
*
|
|
21
|
-
* @prop {
|
|
22
|
-
*
|
|
23
|
-
* This may be provided as a parameter to the constructor,
|
|
24
|
-
* or auto-selected based on our Javascript environment.
|
|
21
|
+
* @prop {module:@lumjs/tests/harness/plugin} plugin - JS platform plugin
|
|
25
22
|
*
|
|
26
23
|
* @prop {Array} queue - An array of `QueuedTest` instances
|
|
27
24
|
* representing tests that we want to run.
|
|
@@ -33,20 +30,17 @@ const Plugin = require('./plugin');
|
|
|
33
30
|
*
|
|
34
31
|
* @exports module:@lumjs/tests/harness
|
|
35
32
|
*/
|
|
36
|
-
class
|
|
33
|
+
class LumTestsHarness
|
|
37
34
|
{
|
|
38
35
|
/**
|
|
39
36
|
* Build a new Harness
|
|
40
37
|
*
|
|
41
|
-
* @param {
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* This is probably not needed. We'll generate a new `Stats` instance.
|
|
38
|
+
* @param {module:@lumjs/tests/harness/plugin} plugin
|
|
39
|
+
* The plugin for the current JS environment.
|
|
45
40
|
*
|
|
46
|
-
*
|
|
41
|
+
* You can pass an instance `object`, or a class constructor `function`.
|
|
47
42
|
*
|
|
48
|
-
*
|
|
49
|
-
* plugin to use based on the JS environment.
|
|
43
|
+
* @param {object} [opts] Advanced Options
|
|
50
44
|
*
|
|
51
45
|
* @param {boolean} [opts.plannedFailure=true] Is a broken plan a failure?
|
|
52
46
|
*
|
|
@@ -63,9 +57,35 @@ class Harness
|
|
|
63
57
|
*
|
|
64
58
|
* If this is `false` then no warning will be shown.
|
|
65
59
|
*
|
|
60
|
+
* @param {boolean} [opts.registerGlobal=false] Add a global variable?
|
|
61
|
+
*
|
|
62
|
+
* If this is `true`, a global variable called `__lum_tests_harness__`
|
|
63
|
+
* will be registered as a pointer to this instance.
|
|
64
|
+
*
|
|
65
|
+
* @param {module:@lumjs/tests/test/stats} [opts.testSuite] Meta-test
|
|
66
|
+
*
|
|
67
|
+
* This option is generally for internal (mostly debugging) use only.
|
|
68
|
+
* Don't use it, just let the constructor build a new `Stats` instance.
|
|
69
|
+
*
|
|
66
70
|
*/
|
|
67
|
-
constructor(opts={})
|
|
71
|
+
constructor(plugin, opts={})
|
|
68
72
|
{
|
|
73
|
+
if (typeof plugin === F && Plugin.isPrototypeOf(plugin))
|
|
74
|
+
{ // Class constructor was passed, build an instance.
|
|
75
|
+
plugin = new plugin();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (plugin instanceof Plugin)
|
|
79
|
+
{ // An explicit plugin was specified.
|
|
80
|
+
this.plugin = plugin;
|
|
81
|
+
plugin.harness = this;
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
{ // That's not valid...
|
|
85
|
+
console.error({plugin, opts, harness: this});
|
|
86
|
+
throw new TypeError('Invalid Plugin specified');
|
|
87
|
+
}
|
|
88
|
+
|
|
69
89
|
// Save a reference to the options.
|
|
70
90
|
this.opts = opts;
|
|
71
91
|
|
|
@@ -78,23 +98,6 @@ class Harness
|
|
|
78
98
|
this.testSuite = new Stats(opts.testSuite);
|
|
79
99
|
}
|
|
80
100
|
|
|
81
|
-
if (opts.plugin instanceof Plugin)
|
|
82
|
-
{
|
|
83
|
-
this.plugin = opts.plugin;
|
|
84
|
-
}
|
|
85
|
-
else if (ctx.isBrowser)
|
|
86
|
-
{
|
|
87
|
-
this.plugin = require('./browser').new(this);
|
|
88
|
-
}
|
|
89
|
-
else if (ctx.isNode)
|
|
90
|
-
{
|
|
91
|
-
this.plugin = require('./node').new(this);
|
|
92
|
-
}
|
|
93
|
-
else
|
|
94
|
-
{
|
|
95
|
-
throw new Error('Could not determine plugin');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
101
|
//console.debug("# ~ plugin", this.plugin, this);
|
|
99
102
|
|
|
100
103
|
this.queue = []; // Tests to be ran.
|
|
@@ -105,6 +108,11 @@ class Harness
|
|
|
105
108
|
this.plannedFailure = opts.plannedFailure ?? true;
|
|
106
109
|
this.plannedWarning = opts.plannedWarning ?? true;
|
|
107
110
|
|
|
111
|
+
if (opts.registerGlobal)
|
|
112
|
+
{ // Add a global variable.
|
|
113
|
+
globalThis[Stats.HARNESS_GLOBAL] = this;
|
|
114
|
+
}
|
|
115
|
+
|
|
108
116
|
} // constructor()
|
|
109
117
|
|
|
110
118
|
/**
|
|
@@ -177,9 +185,9 @@ class Harness
|
|
|
177
185
|
* Run all the queued tests
|
|
178
186
|
*
|
|
179
187
|
* @param {boolean} [plan=true] Set a test plan for the entire suite
|
|
180
|
-
* @returns {object} `this`
|
|
188
|
+
* @returns {Promise<object>} Resolves to `this`
|
|
181
189
|
*/
|
|
182
|
-
run(plan=true)
|
|
190
|
+
async run(plan=true)
|
|
183
191
|
{
|
|
184
192
|
if (plan)
|
|
185
193
|
{
|
|
@@ -188,15 +196,15 @@ class Harness
|
|
|
188
196
|
|
|
189
197
|
for (const queued of this.queue)
|
|
190
198
|
{
|
|
191
|
-
this.runTest(queued);
|
|
199
|
+
await this.runTest(queued);
|
|
192
200
|
}
|
|
193
201
|
|
|
194
202
|
this.testSuite.done();
|
|
195
203
|
|
|
196
|
-
return this
|
|
204
|
+
return this
|
|
197
205
|
}
|
|
198
206
|
|
|
199
|
-
runTest(queued)
|
|
207
|
+
async runTest(queued)
|
|
200
208
|
{
|
|
201
209
|
const name = queued.filename;
|
|
202
210
|
let test;
|
|
@@ -218,22 +226,42 @@ class Harness
|
|
|
218
226
|
}
|
|
219
227
|
else
|
|
220
228
|
{ // Evaluate if the test is successful or not.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
if (test.waiting)
|
|
230
|
+
{ // async tests running
|
|
231
|
+
try
|
|
232
|
+
{
|
|
233
|
+
await test.wait();
|
|
234
|
+
this.testTest(test, name);
|
|
235
|
+
}
|
|
236
|
+
catch (err)
|
|
237
|
+
{
|
|
238
|
+
this.testSuite.fail(name, err);
|
|
239
|
+
}
|
|
229
240
|
}
|
|
230
|
-
else
|
|
231
|
-
{
|
|
232
|
-
|
|
241
|
+
else
|
|
242
|
+
{
|
|
243
|
+
this.testTest(test, name);
|
|
233
244
|
}
|
|
234
245
|
}
|
|
235
246
|
|
|
236
|
-
return
|
|
247
|
+
return test;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
testTest(test, name)
|
|
251
|
+
{
|
|
252
|
+
const ok = this.plugin.ok(test);
|
|
253
|
+
if (typeof ok === B)
|
|
254
|
+
{ // A simple boolean response
|
|
255
|
+
this.testSuite.ok(ok, name);
|
|
256
|
+
}
|
|
257
|
+
else if (ok instanceof Error)
|
|
258
|
+
{ // An error was returned
|
|
259
|
+
this.testSuite.fail(name, ok);
|
|
260
|
+
}
|
|
261
|
+
else
|
|
262
|
+
{ // This should never happen...
|
|
263
|
+
throw new Error("Invalid result from plugin.ok() !!");
|
|
264
|
+
}
|
|
237
265
|
}
|
|
238
266
|
|
|
239
267
|
tap()
|
|
@@ -248,12 +276,12 @@ class Harness
|
|
|
248
276
|
|
|
249
277
|
} // Harness class
|
|
250
278
|
|
|
251
|
-
def(
|
|
252
|
-
def(
|
|
253
|
-
def(
|
|
279
|
+
def(LumTestsHarness, 'Plugin', Plugin);
|
|
280
|
+
def(LumTestsHarness, 'QueuedTest', QueuedTest);
|
|
281
|
+
def(LumTestsHarness, 'Errors', require('./errors'));
|
|
254
282
|
|
|
255
283
|
// Export it.
|
|
256
|
-
module.exports =
|
|
284
|
+
module.exports = LumTestsHarness;
|
|
257
285
|
|
|
258
286
|
// Some classes required at end for recursive sanity reasons.
|
|
259
287
|
|
package/lib/harness/parser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {S,N,needType,isArray
|
|
1
|
+
const {S,N,needType,isArray} = require('@lumjs/core/types');
|
|
2
2
|
const Stats = require('../test/stats');
|
|
3
3
|
const Tap = require('../grammar/tap');
|
|
4
4
|
|
|
@@ -21,7 +21,7 @@ const T = 'test',
|
|
|
21
21
|
*
|
|
22
22
|
* @exports module:@lumjs/tests/harness/parser
|
|
23
23
|
*/
|
|
24
|
-
class
|
|
24
|
+
class LumTestParser
|
|
25
25
|
{
|
|
26
26
|
/**
|
|
27
27
|
* Build a TAP Parser.
|
|
@@ -145,9 +145,9 @@ class Parser
|
|
|
145
145
|
|
|
146
146
|
static new(opts)
|
|
147
147
|
{
|
|
148
|
-
return new
|
|
148
|
+
return new LumTestParser(opts);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// Export the class as the parser.
|
|
153
|
-
module.exports =
|
|
153
|
+
module.exports = LumTestParser;
|
|
@@ -1,34 +1,28 @@
|
|
|
1
1
|
const core = require('@lumjs/core');
|
|
2
|
-
const {
|
|
3
|
-
const {TestFailure,PlanFailure} = require('
|
|
2
|
+
const {AbstractError} = core;
|
|
3
|
+
const {TestFailure,PlanFailure} = require('../errors');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* An abstract base class for Harness plugins
|
|
7
7
|
*
|
|
8
|
-
* @
|
|
8
|
+
* @prop {module:@lumjs/tests/harness} harness
|
|
9
|
+
* The harness instance will assign itself to this property.
|
|
10
|
+
*
|
|
11
|
+
* @exports module:@lumjs/tests/harness/plugin
|
|
9
12
|
*/
|
|
10
|
-
class Plugin
|
|
13
|
+
class Plugin
|
|
11
14
|
{
|
|
12
|
-
/**
|
|
13
|
-
* (Internal class constructor)
|
|
14
|
-
* @param {module:@lumjs/tests/harness} harness - Parent `Harness` instance
|
|
15
|
-
*/
|
|
16
|
-
constructor(harness)
|
|
17
|
-
{
|
|
18
|
-
super();
|
|
19
|
-
this.harness = harness;
|
|
20
|
-
this.$needs('run');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
15
|
/**
|
|
24
16
|
* (ABSTRACT METHOD) Run a test
|
|
25
17
|
*
|
|
26
18
|
* This method must be implemented by every plugin.
|
|
27
19
|
*
|
|
28
|
-
* @name module:@lumjs/tests/harness~Plugin#run
|
|
29
|
-
* @function
|
|
30
20
|
* @param {object} queued - A queued test object
|
|
31
21
|
*/
|
|
22
|
+
run(queued)
|
|
23
|
+
{
|
|
24
|
+
throw new AbstractError("run() method not implemented");
|
|
25
|
+
}
|
|
32
26
|
|
|
33
27
|
/**
|
|
34
28
|
* See if a test had failures
|
|
@@ -67,11 +61,6 @@ class Plugin extends AbstractClass
|
|
|
67
61
|
return true;
|
|
68
62
|
}
|
|
69
63
|
|
|
70
|
-
static new(harness)
|
|
71
|
-
{
|
|
72
|
-
return new this(harness);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
64
|
}
|
|
76
65
|
|
|
77
66
|
module.exports = Plugin;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Node.js harness plugin
|
|
2
2
|
const core = require('@lumjs/core');
|
|
3
3
|
const {N} = core.types;
|
|
4
|
-
const Plugin = require('./
|
|
4
|
+
const Plugin = require('./index');
|
|
5
5
|
const cp = require('node:child_process');
|
|
6
6
|
const getCwd = require('node:process').cwd;
|
|
7
7
|
const fs = require('node:fs');
|
|
@@ -10,8 +10,8 @@ const path = require('node:path');
|
|
|
10
10
|
/**
|
|
11
11
|
* Node.js Harness plugin
|
|
12
12
|
*
|
|
13
|
-
* @exports module:@lumjs/tests/harness
|
|
14
|
-
* @extends module:@lumjs/tetss/harness
|
|
13
|
+
* @exports module:@lumjs/tests/harness/plugin/node
|
|
14
|
+
* @extends module:@lumjs/tetss/harness/plugin
|
|
15
15
|
*/
|
|
16
16
|
class NodePlugin extends Plugin
|
|
17
17
|
{
|
package/lib/test/functional.js
CHANGED
|
@@ -56,11 +56,22 @@ function functional(opts={}, testClass=Test)
|
|
|
56
56
|
const proxyMethods = testClass.$METHODS.all;
|
|
57
57
|
const test = new testClass(opts);
|
|
58
58
|
const functions = { test };
|
|
59
|
-
for (const
|
|
59
|
+
for (const mdef of proxyMethods)
|
|
60
60
|
{
|
|
61
|
-
|
|
61
|
+
let sname, tname;
|
|
62
|
+
if (Array.isArray(mdef))
|
|
63
|
+
{ // separate source and target names
|
|
64
|
+
sname = mdef[0];
|
|
65
|
+
tname = mdef[1];
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
{ // same names for both
|
|
69
|
+
sname = tname = mdef;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
functions[tname] = function ()
|
|
62
73
|
{
|
|
63
|
-
return test[
|
|
74
|
+
return test[sname](...arguments);
|
|
64
75
|
}
|
|
65
76
|
}
|
|
66
77
|
return functions;
|
package/lib/test/index.js
CHANGED
|
@@ -9,12 +9,13 @@ const TEST_METHODS =
|
|
|
9
9
|
[
|
|
10
10
|
'ok', 'call', 'callIs', 'fail', 'pass', 'dies', 'diesWith', 'lives', 'cmp',
|
|
11
11
|
'is', 'isnt', 'isa', 'nota', 'isJSON', 'isntJSON', 'matches', 'skip',
|
|
12
|
+
['async', 'callAsync'],
|
|
12
13
|
];
|
|
13
14
|
|
|
14
15
|
// A list of other methods to export that are not standard tests.
|
|
15
16
|
const META_METHODS =
|
|
16
17
|
[
|
|
17
|
-
'plan', 'diag', 'run', 'tap', 'output', 'done',
|
|
18
|
+
'plan', 'diag', 'run', 'tap', 'output', 'done', 'wait',
|
|
18
19
|
];
|
|
19
20
|
|
|
20
21
|
// The function that powers `Test.call()` and friends.
|
|
@@ -40,10 +41,10 @@ function $call (testfunc, args)
|
|
|
40
41
|
* Raku's Test libraries.
|
|
41
42
|
*
|
|
42
43
|
* @exports module:@lumjs/tests/test
|
|
43
|
-
* @extends module:@lumjs/tests/test
|
|
44
|
+
* @extends module:@lumjs/tests/test/stats
|
|
44
45
|
*
|
|
45
46
|
*/
|
|
46
|
-
class
|
|
47
|
+
class LumTest extends Stats
|
|
47
48
|
{
|
|
48
49
|
/**
|
|
49
50
|
* Build a new Test instance.
|
|
@@ -621,16 +622,16 @@ class Test extends Stats
|
|
|
621
622
|
} // class Test
|
|
622
623
|
|
|
623
624
|
// May want this for sub-classes.
|
|
624
|
-
def(
|
|
625
|
+
def(LumTest, 'Stats', Stats);
|
|
625
626
|
|
|
626
627
|
// Should never need this, but...
|
|
627
|
-
def(
|
|
628
|
+
def(LumTest, 'Log', Log);
|
|
628
629
|
|
|
629
630
|
// Probably don't need this either, but...
|
|
630
|
-
def(
|
|
631
|
+
def(LumTest, '$call', $call);
|
|
631
632
|
|
|
632
633
|
// Methods we're exporting for the 'functional' API.
|
|
633
|
-
def(
|
|
634
|
+
def(LumTest, '$METHODS',
|
|
634
635
|
{
|
|
635
636
|
test: TEST_METHODS,
|
|
636
637
|
meta: META_METHODS,
|
|
@@ -685,4 +686,4 @@ def(Test, '$METHODS',
|
|
|
685
686
|
}); // Test.$METHODS
|
|
686
687
|
|
|
687
688
|
// Export the class
|
|
688
|
-
module.exports =
|
|
689
|
+
module.exports = LumTest;
|
package/lib/test/log.js
CHANGED
|
@@ -22,7 +22,7 @@ const {S,O,isArray,stringify,def} = types;
|
|
|
22
22
|
* @property {object} [details.info] An optional array of extra information.
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
|
-
class
|
|
25
|
+
class LumTestLog
|
|
26
26
|
{
|
|
27
27
|
/**
|
|
28
28
|
* (Internal Constructor)
|
|
@@ -117,4 +117,4 @@ class Log
|
|
|
117
117
|
} // class Log
|
|
118
118
|
|
|
119
119
|
// Export the class itself.
|
|
120
|
-
module.exports =
|
|
120
|
+
module.exports = LumTestLog;
|
package/lib/test/stats.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
const core = require('@lumjs/core');
|
|
2
2
|
const mods = require('@lumjs/core/modules');
|
|
3
3
|
const {types} = core;
|
|
4
|
-
const {S,N,isObj,def} = types;
|
|
4
|
+
const {S,N,F,isObj,def} = types;
|
|
5
5
|
const Log = require('./log');
|
|
6
|
+
const HARNESS_GLOBAL = '__lum_tests_harness__';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_ASYNC_OPTS =
|
|
9
|
+
{
|
|
10
|
+
timeout: 30000,
|
|
11
|
+
interval: 5,
|
|
12
|
+
}
|
|
6
13
|
|
|
7
14
|
/**
|
|
8
15
|
* Minimalistic base class for the `Test` library.
|
|
@@ -11,7 +18,7 @@ const Log = require('./log');
|
|
|
11
18
|
* It's extremely minimal and does not provide the core testing methods.
|
|
12
19
|
* See the [Test]{@link module:@lumjs/tests/test} class for that.
|
|
13
20
|
*
|
|
14
|
-
* @alias module:@lumjs/tests/test
|
|
21
|
+
* @alias module:@lumjs/tests/test/stats
|
|
15
22
|
*
|
|
16
23
|
* @property {number} planned - Number of tests planned, `0` if unplanned.
|
|
17
24
|
* @property {number} failed - Number of tests that failed.
|
|
@@ -21,7 +28,7 @@ const Log = require('./log');
|
|
|
21
28
|
* @property {boolean} isTop - Test module was loaded from the command line.
|
|
22
29
|
* @property {?object} harness - The top-level `Harness` if one was found.
|
|
23
30
|
*/
|
|
24
|
-
class
|
|
31
|
+
class LumTestStats
|
|
25
32
|
{
|
|
26
33
|
/**
|
|
27
34
|
* Build a new instance.
|
|
@@ -42,6 +49,10 @@ class Stats
|
|
|
42
49
|
* @param {number} [opts.stringify=1] The depth `stringify()` should recurse
|
|
43
50
|
* objects and Arrays before switching to plain JSON stringification.
|
|
44
51
|
*
|
|
52
|
+
* @param {object} [opts.async] Options for async tests
|
|
53
|
+
* @param {number} [opts.async.timeout=30000] Max time to wait (in ms)
|
|
54
|
+
* @param {number} [opts.async.interval=5] Status check every (in ms)
|
|
55
|
+
*
|
|
45
56
|
*/
|
|
46
57
|
constructor (opts={})
|
|
47
58
|
{
|
|
@@ -74,6 +85,9 @@ class Stats
|
|
|
74
85
|
this.failed = 0;
|
|
75
86
|
this.skipped = 0;
|
|
76
87
|
this.planned = 0;
|
|
88
|
+
this.waiting = 0;
|
|
89
|
+
|
|
90
|
+
this.asyncOpts = Object.assign({}, DEFAULT_ASYNC_OPTS, opts.async);
|
|
77
91
|
|
|
78
92
|
// The test logs for each unit.
|
|
79
93
|
this.log = [];
|
|
@@ -100,15 +114,18 @@ class Stats
|
|
|
100
114
|
}
|
|
101
115
|
|
|
102
116
|
if (!this.isTop)
|
|
103
|
-
{ // Try to find a Harness instance
|
|
104
|
-
if (isObj(require.main) && require.main.exports
|
|
117
|
+
{ // Try to find a possible Harness instance using some obscure logic...
|
|
118
|
+
if (isObj(require.main) && isObj(require.main.exports)
|
|
119
|
+
&& require.main.exports.constructor.name === 'LumTestsHarness')
|
|
105
120
|
{ // We found the Harness instance.
|
|
106
121
|
this.harness = require.main.exports;
|
|
107
122
|
}
|
|
123
|
+
else if (isObj(globalThis[HARNESS_GLOBAL]))
|
|
124
|
+
{
|
|
125
|
+
this.harness = globalThis[HARNESS_GLOBAL];
|
|
126
|
+
}
|
|
108
127
|
}
|
|
109
|
-
|
|
110
|
-
// Finally, mark the test stats as not generated yet.
|
|
111
|
-
def(this, '$done', false);
|
|
128
|
+
|
|
112
129
|
}
|
|
113
130
|
|
|
114
131
|
// Internal method.
|
|
@@ -276,6 +293,83 @@ class Stats
|
|
|
276
293
|
this.log.push(msg);
|
|
277
294
|
}
|
|
278
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Run async test code
|
|
298
|
+
*
|
|
299
|
+
* If a test needs to wait for the results of an async operation,
|
|
300
|
+
* then this is the way to do it. This method does not actually
|
|
301
|
+
* call any testing methods, that needs to be done inside the `call`.
|
|
302
|
+
*
|
|
303
|
+
* This increments `this.waiting`, which will be decremented when
|
|
304
|
+
* the `Promise` returned by the `call` is resolved or rejected.
|
|
305
|
+
*
|
|
306
|
+
* @param {(function|Promise)} call - Async code to wait for
|
|
307
|
+
*
|
|
308
|
+
* The `function` must be `async` or return a `Promise`.
|
|
309
|
+
* The function will have this Stats instance applied as `this`.
|
|
310
|
+
*
|
|
311
|
+
* @param {...any} args - Arguments for `call` function.
|
|
312
|
+
*
|
|
313
|
+
* @returns {Promise}
|
|
314
|
+
*/
|
|
315
|
+
async async(call, ...args)
|
|
316
|
+
{
|
|
317
|
+
let testPromise;
|
|
318
|
+
this.waiting++;
|
|
319
|
+
|
|
320
|
+
if (typeof call === F)
|
|
321
|
+
{ // It MUST be an async function, OR return a Promise.
|
|
322
|
+
testPromise = call.apply(this, args);
|
|
323
|
+
}
|
|
324
|
+
else
|
|
325
|
+
{ // Assume the promise was passed directly.
|
|
326
|
+
testPromise = call;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (testPromise instanceof Promise)
|
|
330
|
+
{
|
|
331
|
+
testPromise.finally(() => this.waiting--);
|
|
332
|
+
}
|
|
333
|
+
else
|
|
334
|
+
{
|
|
335
|
+
throw new TypeError("async() call must return a Promise");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return testPromise;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Called if `this.waiting` is greater than `0`
|
|
343
|
+
*
|
|
344
|
+
* @returns {Promise<object>}
|
|
345
|
+
*
|
|
346
|
+
* Will resolve to `this` when `this.waiting` becomes `0`.
|
|
347
|
+
* Will be rejected if the maximum wait timeout is reached.
|
|
348
|
+
*/
|
|
349
|
+
wait()
|
|
350
|
+
{
|
|
351
|
+
const ao = this.asyncOpts;
|
|
352
|
+
return new Promise((resolve, reject) =>
|
|
353
|
+
{
|
|
354
|
+
const timeout = setTimeout(() =>
|
|
355
|
+
{
|
|
356
|
+
clearInterval(test);
|
|
357
|
+
clearTimeout(timeout);
|
|
358
|
+
reject(new Error("Timed out waiting for async encoding"));
|
|
359
|
+
}, ao.timeout);
|
|
360
|
+
|
|
361
|
+
const test = setInterval(() =>
|
|
362
|
+
{
|
|
363
|
+
if (this.waiting === 0)
|
|
364
|
+
{
|
|
365
|
+
clearInterval(test);
|
|
366
|
+
clearTimeout(timeout);
|
|
367
|
+
resolve(this);
|
|
368
|
+
}
|
|
369
|
+
}, ao.interval);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
279
373
|
/**
|
|
280
374
|
* Return TAP formatted output for all the tests.
|
|
281
375
|
*
|
|
@@ -359,28 +453,33 @@ class Stats
|
|
|
359
453
|
}
|
|
360
454
|
|
|
361
455
|
/**
|
|
362
|
-
*
|
|
456
|
+
* Run this when you're done testing.
|
|
363
457
|
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
* If
|
|
458
|
+
* It doesn't do much at all if a `Harness` is in use.
|
|
459
|
+
*
|
|
460
|
+
* If no `Harness` is in use, this will call `this.output()`.
|
|
461
|
+
* If there are any async calls we're waiting on this will wait
|
|
462
|
+
* until they are complete before calling `this.output()`.
|
|
368
463
|
*/
|
|
369
464
|
done ()
|
|
370
465
|
{
|
|
371
|
-
if (this
|
|
466
|
+
if (this.harness)
|
|
467
|
+
{ // The harness will handle the rest.
|
|
468
|
+
return this;
|
|
469
|
+
}
|
|
470
|
+
else if (this.waiting)
|
|
372
471
|
{
|
|
373
|
-
|
|
472
|
+
this.wait().finally(() => this.done());
|
|
473
|
+
}
|
|
474
|
+
else
|
|
475
|
+
{
|
|
476
|
+
this.output();
|
|
374
477
|
}
|
|
375
|
-
def(this, '$done', true);
|
|
376
|
-
return (this.harness ? this : this.output());
|
|
377
478
|
}
|
|
378
479
|
|
|
379
|
-
} // class
|
|
480
|
+
} // class Stats
|
|
380
481
|
|
|
381
|
-
|
|
382
|
-
module.exports = Stats;
|
|
482
|
+
def(LumTestStats, 'HARNESS_GLOBAL', HARNESS_GLOBAL);
|
|
383
483
|
|
|
384
|
-
//
|
|
385
|
-
|
|
386
|
-
const Harness = require('../harness');
|
|
484
|
+
// Export the class
|
|
485
|
+
module.exports = LumTestStats;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumjs/tests",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"bin":
|
|
6
6
|
{
|
|
@@ -11,8 +11,11 @@
|
|
|
11
11
|
".": "./lib/index.js",
|
|
12
12
|
"./test": "./lib/test/index.js",
|
|
13
13
|
"./test/functional": "./lib/test/functional.js",
|
|
14
|
+
"./test/stats": "./lib/test/stats.js",
|
|
14
15
|
"./harness": "./lib/harness/index.js",
|
|
15
16
|
"./harness/parser": "./lib/harness/parser.js",
|
|
17
|
+
"./harness/plugin": "./lib/harness/plugin/index.js",
|
|
18
|
+
"./harness/plugin/node": "./lib/harness/plugin/node.js",
|
|
16
19
|
"./package.json": "./package.json",
|
|
17
20
|
"./data/*": "./test/data/*.js"
|
|
18
21
|
},
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
},
|
|
25
28
|
"dependencies":
|
|
26
29
|
{
|
|
27
|
-
"@lumjs/core": "^1.
|
|
30
|
+
"@lumjs/core": "^1.26.0"
|
|
28
31
|
},
|
|
29
32
|
"scripts": {
|
|
30
33
|
"test": "./bin/lumtest.js",
|
package/lib/harness/browser.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// Browser harness plugin
|
|
2
|
-
const Plugin = require('./plugin');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Browser Harness plugin
|
|
6
|
-
*
|
|
7
|
-
* @exports module:@lumjs/tests/harness~BrowserPlugin
|
|
8
|
-
* @extends module:@lumjs/tetss/harness~Plugin
|
|
9
|
-
*/
|
|
10
|
-
class BrowserPlugin extends Plugin
|
|
11
|
-
{
|
|
12
|
-
run(queued)
|
|
13
|
-
{
|
|
14
|
-
throw new Error("Browser tests not supported yet");
|
|
15
|
-
}
|
|
16
|
-
} // Node plugin class
|
|
17
|
-
|
|
18
|
-
module.exports = BrowserPlugin;
|