@lumjs/tests 1.5.0 → 1.7.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 -2
- package/TODO.md +2 -3
- package/bin/lumtest.js +65 -0
- package/lib/grammar/README.md +6 -0
- package/lib/grammar/tap.js +996 -0
- package/lib/harness/browser.js +18 -0
- package/lib/harness/errors.js +44 -0
- package/lib/harness/index.js +260 -0
- package/lib/harness/node.js +71 -0
- package/lib/harness/parser.js +153 -0
- package/lib/harness/plugin.js +77 -0
- package/lib/harness/queuedtest.js +14 -0
- package/lib/index.js +19 -14
- package/lib/{functional.js → test/functional.js} +21 -14
- package/lib/{test.js → test/index.js} +64 -296
- package/lib/{log.js → test/log.js} +31 -15
- package/lib/test/stats.js +385 -0
- package/package.json +22 -13
- package/lib/harness.js +0 -25
- package/test/basics.js +0 -50
- package/test/data/people.js +0 -58
- package/test/dies.js +0 -17
- package/test/functional_basics.js +0 -54
- package/test/functional_dies.js +0 -17
- package/test/functional_isa.js +0 -23
- package/test/inc/basics.js +0 -72
- package/test/inc/dies.js +0 -78
- package/test/inc/isa.js +0 -110
- package/test/isa.js +0 -23
|
@@ -0,0 +1,18 @@
|
|
|
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;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* An error for when a test set had failures
|
|
4
|
+
*
|
|
5
|
+
* @prop {number} failed - The number of tests that failed
|
|
6
|
+
*
|
|
7
|
+
* @exports module:@lumjs/tests/harness~TestFailure
|
|
8
|
+
* @extends Error
|
|
9
|
+
*/
|
|
10
|
+
class TestFailure extends Error
|
|
11
|
+
{
|
|
12
|
+
constructor(failed)
|
|
13
|
+
{
|
|
14
|
+
super(`${failed} tests failed`);
|
|
15
|
+
this.name = 'TestFailure';
|
|
16
|
+
this.failed = failed;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* An error for when the test plan was broken
|
|
22
|
+
*
|
|
23
|
+
* @prop {number} planned - The number of tests planned
|
|
24
|
+
* @prop {number} ran - The number of tests actually ran
|
|
25
|
+
*
|
|
26
|
+
* @exports module:@lumjs/tests/harness~PlanFailure
|
|
27
|
+
* @extends Error
|
|
28
|
+
*/
|
|
29
|
+
class PlanFailure extends Error
|
|
30
|
+
{
|
|
31
|
+
constructor(planned, ran)
|
|
32
|
+
{
|
|
33
|
+
super(`${planned} tests planned; ${ran} tests ran`);
|
|
34
|
+
this.name = 'PlanFailure';
|
|
35
|
+
this.planned = planned;
|
|
36
|
+
this.ran = ran;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Export all of our custom errors.
|
|
41
|
+
module.exports =
|
|
42
|
+
{
|
|
43
|
+
TestFailure, PlanFailure,
|
|
44
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
const core = require('@lumjs/core');
|
|
2
|
+
const {def,lazy,context:ctx} = core;
|
|
3
|
+
const {B,F} = core.types;
|
|
4
|
+
const QueuedTest = require('./queuedtest');
|
|
5
|
+
const Plugin = require('./plugin');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A class that acts as a *test harness* for running other tests.
|
|
9
|
+
*
|
|
10
|
+
* Loosely based off of my PHP `Lum\Test\Harness` class,
|
|
11
|
+
* but with a much more modular design.
|
|
12
|
+
*
|
|
13
|
+
* @prop {object} opts - Options passed to constructor
|
|
14
|
+
*
|
|
15
|
+
* @prop {object} testSuite - A `Stats` (or `Test`) instance
|
|
16
|
+
*
|
|
17
|
+
* This meta-test will keep track of the success or failure of
|
|
18
|
+
* all the other tests. So its `plan` will be equal to the number of
|
|
19
|
+
* tests we're running, and so forth.
|
|
20
|
+
*
|
|
21
|
+
* @prop {object} plugin - A platform-specific `Plugin`
|
|
22
|
+
*
|
|
23
|
+
* This may be provided as a parameter to the constructor,
|
|
24
|
+
* or auto-selected based on our Javascript environment.
|
|
25
|
+
*
|
|
26
|
+
* @prop {Array} queue - An array of `QueuedTest` instances
|
|
27
|
+
* representing tests that we want to run.
|
|
28
|
+
*
|
|
29
|
+
* @prop {Array} stats - An array of `Stats` or `Error` instances
|
|
30
|
+
* representing tests that have been ran.
|
|
31
|
+
*
|
|
32
|
+
* @prop {object} parser - A lazy-loaded `Parser` instance
|
|
33
|
+
*
|
|
34
|
+
* @exports module:@lumjs/tests/harness
|
|
35
|
+
*/
|
|
36
|
+
class Harness
|
|
37
|
+
{
|
|
38
|
+
/**
|
|
39
|
+
* Build a new Harness
|
|
40
|
+
*
|
|
41
|
+
* @param {object} [opts] Options
|
|
42
|
+
* @param {object} [opts.testSuite] A `Test` or `Stats` instance
|
|
43
|
+
*
|
|
44
|
+
* This is probably not needed. We'll generate a new `Stats` instance.
|
|
45
|
+
*
|
|
46
|
+
* @param {object} [opts.plugin] A `Plugin` instance.
|
|
47
|
+
*
|
|
48
|
+
* This is probably not needed. We'll automatically determine a default
|
|
49
|
+
* plugin to use based on the JS environment.
|
|
50
|
+
*
|
|
51
|
+
* @param {boolean} [opts.plannedFailure=true] Is a broken plan a failure?
|
|
52
|
+
*
|
|
53
|
+
* If this is `true` and a test set has a non-zero plan, then the
|
|
54
|
+
* number of tests ran must match the plan or it will be a failure.
|
|
55
|
+
*
|
|
56
|
+
* If this is `false`, then see `opts.plannedWarning` for how we'll
|
|
57
|
+
* report broken plans.
|
|
58
|
+
*
|
|
59
|
+
* @param {boolean} [opts.plannedWarning=true] Show a broken plan warning?
|
|
60
|
+
*
|
|
61
|
+
* If this is `true` and `opts.plannedFailure` is `false` then when
|
|
62
|
+
* a test set has a broken plan we'll send a warning to the JS console.
|
|
63
|
+
*
|
|
64
|
+
* If this is `false` then no warning will be shown.
|
|
65
|
+
*
|
|
66
|
+
*/
|
|
67
|
+
constructor(opts={})
|
|
68
|
+
{
|
|
69
|
+
// Save a reference to the options.
|
|
70
|
+
this.opts = opts;
|
|
71
|
+
|
|
72
|
+
if (opts.testSuite instanceof Stats)
|
|
73
|
+
{ // Set the test suite instance.
|
|
74
|
+
this.testSuite = opts.testSuite;
|
|
75
|
+
}
|
|
76
|
+
else
|
|
77
|
+
{ // Build a new test suite instance.
|
|
78
|
+
this.testSuite = new Stats(opts.testSuite);
|
|
79
|
+
}
|
|
80
|
+
|
|
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
|
+
//console.debug("# ~ plugin", this.plugin, this);
|
|
99
|
+
|
|
100
|
+
this.queue = []; // Tests to be ran.
|
|
101
|
+
this.stats = []; // Results from tests that have been ran
|
|
102
|
+
|
|
103
|
+
lazy(this, 'parser', () => require('./parser').new(this));
|
|
104
|
+
|
|
105
|
+
this.plannedFailure = opts.plannedFailure ?? true;
|
|
106
|
+
this.plannedWarning = opts.plannedWarning ?? true;
|
|
107
|
+
|
|
108
|
+
} // constructor()
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Add `Test` or `Stats` instances that have been run
|
|
112
|
+
* to our list of test stats.
|
|
113
|
+
*
|
|
114
|
+
* @protected
|
|
115
|
+
*
|
|
116
|
+
* @param {object} info - The `QueuedTest` instance
|
|
117
|
+
* @param {?object} result - The returned result
|
|
118
|
+
*
|
|
119
|
+
* Will usually be a `Test` or `Stats` instance, but may
|
|
120
|
+
* be an `Error` if an exception was thrown trying to run the test.
|
|
121
|
+
*
|
|
122
|
+
* @returns {object} `this`
|
|
123
|
+
*/
|
|
124
|
+
addStats(info, stats)
|
|
125
|
+
{
|
|
126
|
+
if (info instanceof QueuedTest
|
|
127
|
+
&& (stats instanceof Stats
|
|
128
|
+
|| stats instanceof Error))
|
|
129
|
+
{
|
|
130
|
+
this.stats[info.name] = {info, stats};
|
|
131
|
+
}
|
|
132
|
+
else
|
|
133
|
+
{
|
|
134
|
+
throw new TypeError("Invalid object type");
|
|
135
|
+
}
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Add a test to our testing queue.
|
|
141
|
+
*
|
|
142
|
+
* @param {string} filename - A filename for the test.
|
|
143
|
+
* @param {object} [opts] Options for the `QueuedTest` instance.
|
|
144
|
+
* @returns {object} `this`
|
|
145
|
+
*/
|
|
146
|
+
addTest(filename, opts={})
|
|
147
|
+
{
|
|
148
|
+
const queued = new QueuedTest(this, filename, opts);
|
|
149
|
+
this.queue.push(queued);
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Add an entire folder of tests.
|
|
155
|
+
*
|
|
156
|
+
* This method may not be implemented by all
|
|
157
|
+
* Harness Plugins. Your mileage may vary.
|
|
158
|
+
*
|
|
159
|
+
* @param {string} dir - The directory name/path
|
|
160
|
+
* @param {object} [opts] Options
|
|
161
|
+
* @returns {object} `this`
|
|
162
|
+
*/
|
|
163
|
+
addDir(dir, opts={})
|
|
164
|
+
{
|
|
165
|
+
if (typeof this.plugin.addDir === F)
|
|
166
|
+
{
|
|
167
|
+
this.plugin.addDir(dir, opts);
|
|
168
|
+
}
|
|
169
|
+
else
|
|
170
|
+
{
|
|
171
|
+
console.warn("The plugin does not support addDir()");
|
|
172
|
+
}
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Run all the queued tests
|
|
178
|
+
*
|
|
179
|
+
* @param {boolean} [plan=true] Set a test plan for the entire suite
|
|
180
|
+
* @returns {object} `this`
|
|
181
|
+
*/
|
|
182
|
+
run(plan=true)
|
|
183
|
+
{
|
|
184
|
+
if (plan)
|
|
185
|
+
{
|
|
186
|
+
this.testSuite.plan(this.queue.length);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const queued of this.queue)
|
|
190
|
+
{
|
|
191
|
+
this.runTest(queued);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.testSuite.done();
|
|
195
|
+
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
runTest(queued)
|
|
200
|
+
{
|
|
201
|
+
const name = queued.filename;
|
|
202
|
+
let test;
|
|
203
|
+
|
|
204
|
+
try
|
|
205
|
+
{
|
|
206
|
+
test = this.plugin.run(queued);
|
|
207
|
+
}
|
|
208
|
+
catch (err)
|
|
209
|
+
{
|
|
210
|
+
test = err;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.addStats(queued, test);
|
|
214
|
+
|
|
215
|
+
if (typeof test === Error)
|
|
216
|
+
{ // Something went wrong.
|
|
217
|
+
this.testSuite.fail(name, test);
|
|
218
|
+
}
|
|
219
|
+
else
|
|
220
|
+
{ // Evaluate if the test is successful or not.
|
|
221
|
+
const ok = this.plugin.ok(test);
|
|
222
|
+
if (typeof ok === B)
|
|
223
|
+
{ // A simple boolean response
|
|
224
|
+
this.testSuite.ok(ok, name);
|
|
225
|
+
}
|
|
226
|
+
else if (ok instanceof Error)
|
|
227
|
+
{ // An error was returned
|
|
228
|
+
this.testSuite.fail(name, ok);
|
|
229
|
+
}
|
|
230
|
+
else
|
|
231
|
+
{ // This should never happen...
|
|
232
|
+
throw new Error("Invalid result from plugin.ok() !!");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
tap()
|
|
240
|
+
{
|
|
241
|
+
return this.testSuite.tap();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
get TAP()
|
|
245
|
+
{
|
|
246
|
+
return this.tap();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
} // Harness class
|
|
250
|
+
|
|
251
|
+
def(Harness, 'Plugin', Plugin);
|
|
252
|
+
def(Harness, 'QueuedTest', QueuedTest);
|
|
253
|
+
def(Harness, 'Errors', require('./errors'));
|
|
254
|
+
|
|
255
|
+
// Export it.
|
|
256
|
+
module.exports = Harness;
|
|
257
|
+
|
|
258
|
+
// Some classes required at end for recursive sanity reasons.
|
|
259
|
+
|
|
260
|
+
const Stats = require('../test/stats');
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Node.js harness plugin
|
|
2
|
+
const core = require('@lumjs/core');
|
|
3
|
+
const {N} = core.types;
|
|
4
|
+
const Plugin = require('./plugin');
|
|
5
|
+
const cp = require('node:child_process');
|
|
6
|
+
const getCwd = require('node:process').cwd;
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Node.js Harness plugin
|
|
12
|
+
*
|
|
13
|
+
* @exports module:@lumjs/tests/harness~NodePlugin
|
|
14
|
+
* @extends module:@lumjs/tetss/harness~Plugin
|
|
15
|
+
*/
|
|
16
|
+
class NodePlugin extends Plugin
|
|
17
|
+
{
|
|
18
|
+
runInternal(queued)
|
|
19
|
+
{
|
|
20
|
+
let name = path.join(getCwd(), queued.filename);
|
|
21
|
+
return require(name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
runExternal(queued)
|
|
25
|
+
{
|
|
26
|
+
const name = queued.filename;
|
|
27
|
+
const args = queued.options.args ?? [];
|
|
28
|
+
const proc = cp.spawnSync(name, args, {encoding: 'utf8'});
|
|
29
|
+
return this.harness.parser.parse(proc.stdout);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
run(queued)
|
|
33
|
+
{
|
|
34
|
+
if (queued.options.external)
|
|
35
|
+
{
|
|
36
|
+
return this.runExternal(queued);
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
{
|
|
40
|
+
return this.runInternal(queued);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
addDir(dir, opts={}, recurse)
|
|
45
|
+
{
|
|
46
|
+
if (typeof recurse !== N && typeof opts.recurse === N)
|
|
47
|
+
{
|
|
48
|
+
recurse = opts.recurse;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const ext = opts.ext ?? '.js';
|
|
52
|
+
const testOpts = opts.test ?? opts;
|
|
53
|
+
const files = fs.readdirSync(dir, {encoding: 'utf8', withFileTypes: true});
|
|
54
|
+
|
|
55
|
+
for (const file of files)
|
|
56
|
+
{
|
|
57
|
+
if (file.name === '.' || file.name === '..') continue;
|
|
58
|
+
if (typeof recurse === N && recurse > 0 && file.isDirectory())
|
|
59
|
+
{ // Recurse to a nested directory.
|
|
60
|
+
this.addDir(path.join(dir, file.name), opts, recurse-1);
|
|
61
|
+
}
|
|
62
|
+
else if (file.isFile() && file.name.endsWith(ext))
|
|
63
|
+
{ // It would seem to be a valid test.
|
|
64
|
+
this.harness.addTest(path.join(dir, file.name), testOpts);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} // Node plugin class
|
|
70
|
+
|
|
71
|
+
module.exports = NodePlugin;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const {S,N,needType,isArray,isObj} = require('@lumjs/core/types');
|
|
2
|
+
const Stats = require('../test/stats');
|
|
3
|
+
const Tap = require('../grammar/tap');
|
|
4
|
+
|
|
5
|
+
const T = 'test',
|
|
6
|
+
I = 'info',
|
|
7
|
+
M = 'meta',
|
|
8
|
+
A = 'item',
|
|
9
|
+
C = 'comment',
|
|
10
|
+
O = 'other';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A simplistic TAP parser.
|
|
14
|
+
*
|
|
15
|
+
* Currently only handles *very basic* TAP v12 output.
|
|
16
|
+
* This is loosely based on the TAP parser in my PHP
|
|
17
|
+
* `Lum\Test\Harness` library, but is split off into
|
|
18
|
+
* its own class, and how it handles the parsed data
|
|
19
|
+
* is quite different. I think I may eventually update
|
|
20
|
+
* the PHP version to be more like this one.
|
|
21
|
+
*
|
|
22
|
+
* @exports module:@lumjs/tests/harness/parser
|
|
23
|
+
*/
|
|
24
|
+
class Parser
|
|
25
|
+
{
|
|
26
|
+
/**
|
|
27
|
+
* Build a TAP Parser.
|
|
28
|
+
* @param {object} harness - The parent Harness
|
|
29
|
+
*/
|
|
30
|
+
constructor(harness)
|
|
31
|
+
{
|
|
32
|
+
this.harness = harness;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse a multiline TAP string
|
|
37
|
+
*
|
|
38
|
+
* @param {string} tapSource - The full TAP string to parse.
|
|
39
|
+
*
|
|
40
|
+
* @returns {object} A simple `Test`-like object with the parsed results.
|
|
41
|
+
*/
|
|
42
|
+
parse(tapSource)
|
|
43
|
+
{
|
|
44
|
+
needType(S, tapSource);
|
|
45
|
+
|
|
46
|
+
// Our base Test for populating values into.
|
|
47
|
+
const test = new Stats();
|
|
48
|
+
|
|
49
|
+
// An AST that we'll use to populate the stats.
|
|
50
|
+
const tap = Tap.parse(tapSource);
|
|
51
|
+
|
|
52
|
+
// Test the first line for a plan.
|
|
53
|
+
if (typeof tap.plan === N)
|
|
54
|
+
{
|
|
55
|
+
test.plan(tap.plan);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (tap.items.length === 0) return test; // No lines, cannot continue.
|
|
59
|
+
|
|
60
|
+
// A structure to store our parsing state.
|
|
61
|
+
const cur =
|
|
62
|
+
{
|
|
63
|
+
log: null,
|
|
64
|
+
pos: -1,
|
|
65
|
+
cmd: null,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const unhandled = line => console.log("unhandled log line", {line, cur});
|
|
69
|
+
|
|
70
|
+
for (const line of tap.lines)
|
|
71
|
+
{
|
|
72
|
+
if (line.type === T)
|
|
73
|
+
{ // A regular test line; will increment the counter
|
|
74
|
+
if (line.skip)
|
|
75
|
+
{
|
|
76
|
+
cur.log = test.skip(line.comment, line.desc);
|
|
77
|
+
}
|
|
78
|
+
else if (line.todo)
|
|
79
|
+
{
|
|
80
|
+
cur.log = test.todo(line.comment, line.desc);
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
cur.log = test.ok(line.ok, line.desc, line.comment);
|
|
85
|
+
}
|
|
86
|
+
cur.pos++;
|
|
87
|
+
}
|
|
88
|
+
else if (line.type === I && cur.log)
|
|
89
|
+
{ // Log detail fields
|
|
90
|
+
cur.cmd = line.info;
|
|
91
|
+
cur.log.details[line.info] = line.comment;
|
|
92
|
+
}
|
|
93
|
+
else if (line.type === M)
|
|
94
|
+
{ // We skip meta info.
|
|
95
|
+
cur.cmd = null;
|
|
96
|
+
cur.log = null;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
else if (line.type === A && cur.log)
|
|
100
|
+
{ // An array item
|
|
101
|
+
cur.cmd = null;
|
|
102
|
+
if (cur.log.details.info === undefined)
|
|
103
|
+
{
|
|
104
|
+
cur.log.details.info = [];
|
|
105
|
+
}
|
|
106
|
+
cur.log.details.info.push(line.text);
|
|
107
|
+
}
|
|
108
|
+
else if (line.type === C)
|
|
109
|
+
{ // A regular comment
|
|
110
|
+
test.diag(line.text);
|
|
111
|
+
cur.cmd = null;
|
|
112
|
+
cur.log = null;
|
|
113
|
+
cur.pos++;
|
|
114
|
+
}
|
|
115
|
+
else if (line.type === O)
|
|
116
|
+
{ // A line that doesn't match anything else.
|
|
117
|
+
if (cur.log && cur.cmd)
|
|
118
|
+
{ // There's a comment command statement in use.
|
|
119
|
+
const ld = cur.log.details;
|
|
120
|
+
ld[cur.cmd] += "\n" + line.text;
|
|
121
|
+
}
|
|
122
|
+
else if (cur.log && isArray(cur.log.details.info))
|
|
123
|
+
{ // The current log has info, let's add to it.
|
|
124
|
+
const ldi = cur.log.details.info;
|
|
125
|
+
const last = ldi.length - 1;
|
|
126
|
+
ldi[last] += "\n" + line.text;
|
|
127
|
+
}
|
|
128
|
+
else if (cur.pos > -1 && typeof test.log[cur.pos] === S)
|
|
129
|
+
{ // The current entry is a string, let's add to it.
|
|
130
|
+
test.log[cur.pos] += "\n" + line.text;
|
|
131
|
+
}
|
|
132
|
+
else
|
|
133
|
+
{ // Don't know what to do with this.
|
|
134
|
+
unhandled(line);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else
|
|
138
|
+
{ // Can't do anything, so warn about it and move on.
|
|
139
|
+
unhandled(line);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return test;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static new(opts)
|
|
147
|
+
{
|
|
148
|
+
return new Parser(opts);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Export the class as the parser.
|
|
153
|
+
module.exports = Parser;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const core = require('@lumjs/core');
|
|
2
|
+
const {AbstractClass} = core;
|
|
3
|
+
const {TestFailure,PlanFailure} = require('./errors');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* An abstract base class for Harness plugins
|
|
7
|
+
*
|
|
8
|
+
* @exports module:@lumjs/tests/harness~Plugin
|
|
9
|
+
*/
|
|
10
|
+
class Plugin extends AbstractClass
|
|
11
|
+
{
|
|
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
|
+
/**
|
|
24
|
+
* (ABSTRACT METHOD) Run a test
|
|
25
|
+
*
|
|
26
|
+
* This method must be implemented by every plugin.
|
|
27
|
+
*
|
|
28
|
+
* @name module:@lumjs/tests/harness~Plugin#run
|
|
29
|
+
* @function
|
|
30
|
+
* @param {object} queued - A queued test object
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* See if a test had failures
|
|
35
|
+
*
|
|
36
|
+
* @param {object} test - The `Test` or `Stats` object to check
|
|
37
|
+
* @returns {true|Error}
|
|
38
|
+
*
|
|
39
|
+
* - Will return `true` if no failures reported.
|
|
40
|
+
* - Will return a `TestFailure` if the test had failures reported.
|
|
41
|
+
* - May return a `PlanFailure` if the test plan was broken.
|
|
42
|
+
* This only applies if `this.harness.plannedFailure` is `true`.
|
|
43
|
+
*
|
|
44
|
+
*/
|
|
45
|
+
ok(test)
|
|
46
|
+
{
|
|
47
|
+
if (test.failed !== 0)
|
|
48
|
+
{ // The test had failed units.
|
|
49
|
+
return new TestFailure(test.failed);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const planned = test.planned,
|
|
53
|
+
ran = test.ran;
|
|
54
|
+
|
|
55
|
+
const planOk = (planned === 0 || planned === ran);
|
|
56
|
+
|
|
57
|
+
if (this.harness.plannedFailure && !planOk)
|
|
58
|
+
{ // The number of tests ran was not the number of tests planned.
|
|
59
|
+
return new PlanFailure(planned, ran);
|
|
60
|
+
}
|
|
61
|
+
else if (this.harness.plannedWarning && !planOk)
|
|
62
|
+
{ // Report the plan failure to the console log.
|
|
63
|
+
console.warn(new PlanFailure(planned, ran));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// If we reached here, everything looks good.
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static new(harness)
|
|
71
|
+
{
|
|
72
|
+
return new this(harness);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = Plugin;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A class representing a queued test.
|
|
3
|
+
* @exports module:@lumjs/tests/harness~QueuedTest
|
|
4
|
+
*/
|
|
5
|
+
class QueuedTest
|
|
6
|
+
{
|
|
7
|
+
constructor(harness, filename, opts)
|
|
8
|
+
{
|
|
9
|
+
this.harness = harness;
|
|
10
|
+
this.filename = filename;
|
|
11
|
+
this.options = opts;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
module.exports = QueuedTest;
|
package/lib/index.js
CHANGED
|
@@ -2,28 +2,34 @@
|
|
|
2
2
|
* Several test related classes.
|
|
3
3
|
* @module @lumjs/tests
|
|
4
4
|
*/
|
|
5
|
+
const {lazy} = require('@lumjs/core/types');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* Test class
|
|
8
|
-
*
|
|
8
|
+
* The main Test class
|
|
9
|
+
*
|
|
9
10
|
* @see module:@lumjs/tests/test
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
exports.Test = require('./test');
|
|
13
|
+
|
|
14
|
+
const lz = {enumerable: true};
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
|
-
* Test Harness class
|
|
16
|
-
*
|
|
17
|
+
* Test Harness class (lazy loaded)
|
|
18
|
+
*
|
|
19
|
+
* @name module:@lumjs/tests.Harness
|
|
20
|
+
* @class
|
|
17
21
|
* @see module:@lumjs/tests/harness
|
|
18
22
|
*/
|
|
19
|
-
|
|
23
|
+
lazy(exports, 'Harness', () => require('./harness'), lz);
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
|
-
* Functional API registration
|
|
23
|
-
*
|
|
24
|
-
* @
|
|
26
|
+
* Functional API registration (lazy loaded)
|
|
27
|
+
*
|
|
28
|
+
* @name module:@lumjs/tests.functional
|
|
29
|
+
* @function
|
|
30
|
+
* @see module:@lumjs/tests/test/functional
|
|
25
31
|
*/
|
|
26
|
-
|
|
32
|
+
lazy(exports, 'functional', () => require('./test/functional'), lz);
|
|
27
33
|
|
|
28
34
|
/**
|
|
29
35
|
* Create a new Test instance.
|
|
@@ -31,8 +37,7 @@ module.exports.functional = require('./functional');
|
|
|
31
37
|
* @param {object} [opts] Options to pass to the `Test` constructor.
|
|
32
38
|
* @returns {Test} A new test instance.
|
|
33
39
|
*/
|
|
34
|
-
|
|
40
|
+
exports.new = function(opts={})
|
|
35
41
|
{
|
|
36
|
-
return new Test(opts);
|
|
42
|
+
return new exports.Test(opts);
|
|
37
43
|
}
|
|
38
|
-
|