@lumjs/tests 1.0.0 → 1.3.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 +52 -2
- package/TODO.md +5 -2
- package/{src → lib}/functional.js +5 -25
- package/{src → lib}/harness.js +0 -0
- package/lib/index.js +23 -0
- package/{src → lib}/log.js +23 -8
- package/lib/test.js +681 -0
- package/package.json +8 -3
- package/test/basics.js +22 -4
- package/test/dies.js +1 -1
- package/test/functional_basics.js +25 -4
- package/test/functional_dies.js +1 -1
- package/test/functional_isa.js +23 -0
- package/test/inc/basics.js +32 -2
- package/test/inc/isa.js +110 -0
- package/test/isa.js +23 -0
- package/index.js +0 -23
- package/src/test.js +0 -402
package/lib/test.js
ADDED
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
const core = require('@lumjs/core');
|
|
2
|
+
const types = core.types;
|
|
3
|
+
const {F,S,N,isObj,isArray,def} = types;
|
|
4
|
+
|
|
5
|
+
// We use a separate class to represent test logs.
|
|
6
|
+
const Log = require('./log');
|
|
7
|
+
|
|
8
|
+
// A list of Test methods that return Log objects.
|
|
9
|
+
const TEST_METHODS =
|
|
10
|
+
[
|
|
11
|
+
'ok', 'call', 'fail', 'pass', 'dies', 'diesWith', 'lives', 'cmp',
|
|
12
|
+
'is', 'isnt', 'isa', 'nota', 'isJSON', 'isntJSON', 'skip',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// A list of other methods to export that are not standard tests.
|
|
16
|
+
const META_METHODS =
|
|
17
|
+
[
|
|
18
|
+
'plan', 'diag', 'run', 'tap', 'output',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// The function that powers `Test.call()` and friends.
|
|
22
|
+
function $call (testfunc, args)
|
|
23
|
+
{
|
|
24
|
+
const ret = {};
|
|
25
|
+
try
|
|
26
|
+
{
|
|
27
|
+
ret.val = testfunc(...args);
|
|
28
|
+
}
|
|
29
|
+
catch (e)
|
|
30
|
+
{
|
|
31
|
+
ret.err = e;
|
|
32
|
+
}
|
|
33
|
+
return ret;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A simple testing library with TAP support.
|
|
38
|
+
*
|
|
39
|
+
* Based on Lum.php's Test library.
|
|
40
|
+
* Which itself was based on Perl 5's Test::More, and
|
|
41
|
+
* Raku's Test libraries.
|
|
42
|
+
*/
|
|
43
|
+
class Test
|
|
44
|
+
{
|
|
45
|
+
/**
|
|
46
|
+
* Build a new Test instance.
|
|
47
|
+
*
|
|
48
|
+
* @param {object} [opts] Named options.
|
|
49
|
+
* @param {string} [opts.id] A unique test id, used by Harness.
|
|
50
|
+
* @param {number} [opts.plan] Passed to `plan()` method.
|
|
51
|
+
* @param {object} [opts.moduleName] Options for `core.modules.name()`.
|
|
52
|
+
* @param {object} [opts.module] The node module to export this test to.
|
|
53
|
+
*
|
|
54
|
+
* If you use this option, `opts.module.exports` will be assigned
|
|
55
|
+
* to the test instance, overriding anything that may have been
|
|
56
|
+
* assigned to it previously.
|
|
57
|
+
*
|
|
58
|
+
* Also, if this is passed, and `opts.id` was not specified, and id
|
|
59
|
+
* will be auto-generated based on the filename of the module.
|
|
60
|
+
*
|
|
61
|
+
*/
|
|
62
|
+
constructor (opts={})
|
|
63
|
+
{
|
|
64
|
+
if (typeof opts === N)
|
|
65
|
+
{
|
|
66
|
+
opts = {plan: opts};
|
|
67
|
+
}
|
|
68
|
+
else if (typeof opts === S)
|
|
69
|
+
{
|
|
70
|
+
opts = {id: opts};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const hasModule = isObj(opts.module);
|
|
74
|
+
|
|
75
|
+
if (typeof opts.id === S)
|
|
76
|
+
{ // A specific id was specified.
|
|
77
|
+
this.id = opts.id;
|
|
78
|
+
}
|
|
79
|
+
else if (hasModule)
|
|
80
|
+
{ // We're going to generate a simple name.
|
|
81
|
+
this.id = core.modules.name(opts.module, opts.moduleName);
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
{ // An anonymous test.
|
|
85
|
+
this.id = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.failed = 0;
|
|
89
|
+
this.skipped = 0;
|
|
90
|
+
this.planned = 0;
|
|
91
|
+
this.log = [];
|
|
92
|
+
|
|
93
|
+
if (typeof opts.plan === N)
|
|
94
|
+
{
|
|
95
|
+
this.plan(opts.plan);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (hasModule)
|
|
99
|
+
{ // Finally, if a module was passed, its going to export this test.
|
|
100
|
+
opts.module.exports = this;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Indicate how many tests are planned to be run in this set.
|
|
106
|
+
*
|
|
107
|
+
* @param {number} num - The number of tests that should run.
|
|
108
|
+
*/
|
|
109
|
+
plan (num)
|
|
110
|
+
{
|
|
111
|
+
if (typeof num === N)
|
|
112
|
+
{
|
|
113
|
+
this.planned = num;
|
|
114
|
+
}
|
|
115
|
+
else if (num === false)
|
|
116
|
+
{
|
|
117
|
+
this.planned = 0;
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
{
|
|
121
|
+
throw new Error("Invalid value passed to plan()");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check if a value is truthy, and add a log indicating the result.
|
|
127
|
+
*
|
|
128
|
+
* This is the absolute most basic method that every other testing method
|
|
129
|
+
* uses to log the results.
|
|
130
|
+
*
|
|
131
|
+
* I won't document the `desc` and `directive` parameters on any of the
|
|
132
|
+
* other methods as they are exactly the same as here.
|
|
133
|
+
*
|
|
134
|
+
* @param {*} test - Any value, usually the output of a test function.
|
|
135
|
+
* If the value evaluates as `true` (aka a truthy value), the test passes.
|
|
136
|
+
* If it evaluates as `false` (a falsey value), the test fails.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} [desc] A short description of the test.
|
|
139
|
+
* @param {(string|Error)} [directive] Further information for the log.
|
|
140
|
+
* @param {object} [details] Extra details to add to the log.
|
|
141
|
+
* @returns {Log} The test log with the results.
|
|
142
|
+
*/
|
|
143
|
+
ok (test, desc, directive, details)
|
|
144
|
+
{
|
|
145
|
+
const log = new Log();
|
|
146
|
+
|
|
147
|
+
if (test)
|
|
148
|
+
{
|
|
149
|
+
log.ok = true;
|
|
150
|
+
}
|
|
151
|
+
else
|
|
152
|
+
{
|
|
153
|
+
this.failed++;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (typeof desc === S)
|
|
157
|
+
{
|
|
158
|
+
log.desc = desc;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (directive)
|
|
162
|
+
{
|
|
163
|
+
log.directive = directive;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (isObj(details))
|
|
167
|
+
{
|
|
168
|
+
log.details = details;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.log.push(log);
|
|
172
|
+
|
|
173
|
+
return log;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Run a function and pass its return value to `ok()`.
|
|
178
|
+
*
|
|
179
|
+
* @param {function} testfunc - The function to run.
|
|
180
|
+
* If the function returns, the return value will be passed to `ok()`.
|
|
181
|
+
* If it throws an Error, the test will be marked as failed, and the
|
|
182
|
+
* Error will be passed as the `directive` to the `ok()` method.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} [desc] A description for `ok()`.
|
|
185
|
+
* @param {...any} [args] Arguments to pass to the test function.
|
|
186
|
+
* @returns {Log}
|
|
187
|
+
*/
|
|
188
|
+
call(testfunc, desc, ...args)
|
|
189
|
+
{
|
|
190
|
+
const ret = $call(testfunc, args);
|
|
191
|
+
return this.ok(ret.val, desc, ret.err);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Mark a test as failed.
|
|
196
|
+
*
|
|
197
|
+
* @param {string} desc
|
|
198
|
+
* @param {*} [directive]
|
|
199
|
+
* @returns {Log}
|
|
200
|
+
*/
|
|
201
|
+
fail (desc, directive)
|
|
202
|
+
{
|
|
203
|
+
return this.ok(false, desc, directive);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Mark a test as passed.
|
|
208
|
+
*
|
|
209
|
+
* @param {string} desc
|
|
210
|
+
* @param {*} [directive]
|
|
211
|
+
* @returns {Log}
|
|
212
|
+
*/
|
|
213
|
+
pass (desc, directive)
|
|
214
|
+
{
|
|
215
|
+
return this.ok(true, desc, directive);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* See if a function throws an Error.
|
|
220
|
+
*
|
|
221
|
+
* The function will be called in a `try { } catch (err) { }` block.
|
|
222
|
+
*
|
|
223
|
+
* If an error is caught, the test will be considered to have passed,
|
|
224
|
+
* and the `Error` object will be used as the `directive` in the `ok()`
|
|
225
|
+
* call. If no error is caught the test will be considered to have failed.
|
|
226
|
+
*
|
|
227
|
+
* @param {function} testfunc
|
|
228
|
+
* @param {string} [desc]
|
|
229
|
+
* @param {...any} [args]
|
|
230
|
+
* @returns {Log}
|
|
231
|
+
*/
|
|
232
|
+
dies (testfunc, desc, ...args)
|
|
233
|
+
{
|
|
234
|
+
const ret = $call(testfunc, args);
|
|
235
|
+
return this.ok(('err' in ret), desc, ret.err);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* See if a function throws an Error, and if the Error passes a second test.
|
|
240
|
+
*
|
|
241
|
+
* All the notes that apply to `dies()` apply here as well.
|
|
242
|
+
*
|
|
243
|
+
* @param {function} testfunc - Same as `dies()` and `call()`
|
|
244
|
+
* We don't care about the return value of this function.
|
|
245
|
+
* We only care that it *should* throw an Error.
|
|
246
|
+
*
|
|
247
|
+
* @param {(function|string)} testerr - A test to validate the Error.
|
|
248
|
+
*
|
|
249
|
+
* If this is a `function` it will be passed the thrown `Error` as the first
|
|
250
|
+
* parameter, and an `Array` of `args` as the second parameter. The return
|
|
251
|
+
* value from this will be passed to `ok()`.
|
|
252
|
+
*
|
|
253
|
+
* If this is a `string` then the test will pass only if the either the
|
|
254
|
+
* `Error.name` or `Error.message` is exactly equal to this value.
|
|
255
|
+
*
|
|
256
|
+
* @param {string} [desc]
|
|
257
|
+
* @param {...any} [args]
|
|
258
|
+
* @returns {Log}
|
|
259
|
+
*/
|
|
260
|
+
diesWith(testfunc, testerr, desc, ...args)
|
|
261
|
+
{
|
|
262
|
+
let ok = false, details = {}, err = null;
|
|
263
|
+
|
|
264
|
+
const r1 = $call(testfunc, args);
|
|
265
|
+
|
|
266
|
+
if ('err' in r1)
|
|
267
|
+
{
|
|
268
|
+
err = r1.err;
|
|
269
|
+
|
|
270
|
+
if (typeof testerr === F)
|
|
271
|
+
{ // A secondary function to test the error with.
|
|
272
|
+
const r2 = $call(testerr, [err, args]);
|
|
273
|
+
if ('err' in r2)
|
|
274
|
+
{ // Second function threw an error, add it as diagnostic info.
|
|
275
|
+
details.info = r2.err;
|
|
276
|
+
}
|
|
277
|
+
else
|
|
278
|
+
{ // No error, use the function output as the test value.
|
|
279
|
+
ok = r2.val;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else if (typeof testerr === S)
|
|
283
|
+
{ // A simple name/message test.
|
|
284
|
+
if (err.name === testerr || err.message === testerr)
|
|
285
|
+
{ // Either the name or the message matched the string.
|
|
286
|
+
ok = true;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
} // if r1.err
|
|
291
|
+
|
|
292
|
+
return this.ok(ok, desc, err, details);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* See if a function runs without throwing an Error.
|
|
297
|
+
*
|
|
298
|
+
* The function will be called in a `try { } catch (err) { }` block.
|
|
299
|
+
*
|
|
300
|
+
* If an error is caught, the test will be considered to have failed,
|
|
301
|
+
* and the `Error` object will be used as the `directive` in the `ok()`
|
|
302
|
+
* call. If no error is caught the test will be considered to have passed.
|
|
303
|
+
*
|
|
304
|
+
* @param {function} testfunc
|
|
305
|
+
* @param {string} [desc]
|
|
306
|
+
* @param {...any} [args]
|
|
307
|
+
* @returns {Log}
|
|
308
|
+
*/
|
|
309
|
+
lives (testfunc, desc, ...args)
|
|
310
|
+
{
|
|
311
|
+
const ret = $call(testfunc, args);
|
|
312
|
+
return this.ok(!('err' in ret), desc, ret.err);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* See if a value is what we expect it to be.
|
|
317
|
+
*
|
|
318
|
+
* @param {*} got - The result value from the test function.
|
|
319
|
+
* @param {*} want - The value we expected from the test function.
|
|
320
|
+
* @param {string} comp - A comparitor to test with.
|
|
321
|
+
*
|
|
322
|
+
* - `===`, `is` (See also: `is()`)
|
|
323
|
+
* - `!==`, `isnt` (See also: `isnt()`)
|
|
324
|
+
* - `==`, `eq`
|
|
325
|
+
* - `!=`, `ne`
|
|
326
|
+
* - `>`, `gt`
|
|
327
|
+
* - `<`, `lt`
|
|
328
|
+
* - `>=`, `ge`, `gte`
|
|
329
|
+
* - `<=`, `le`, `lte`
|
|
330
|
+
*
|
|
331
|
+
* @param {string} desc
|
|
332
|
+
* @param {boolean} [stringify=true] Stringify values in TAP output?
|
|
333
|
+
* @returns {Log}
|
|
334
|
+
*/
|
|
335
|
+
cmp (got, want, comp, desc, stringify=true)
|
|
336
|
+
{
|
|
337
|
+
let test;
|
|
338
|
+
switch(comp)
|
|
339
|
+
{
|
|
340
|
+
case 'is':
|
|
341
|
+
case '===':
|
|
342
|
+
test = (got === want);
|
|
343
|
+
break;
|
|
344
|
+
case 'isnt':
|
|
345
|
+
case '!==':
|
|
346
|
+
test = (got !== want);
|
|
347
|
+
break;
|
|
348
|
+
case 'eq':
|
|
349
|
+
case '==':
|
|
350
|
+
test = (got == want);
|
|
351
|
+
break;
|
|
352
|
+
case 'ne':
|
|
353
|
+
case '!=':
|
|
354
|
+
test = (got != want);
|
|
355
|
+
break;
|
|
356
|
+
case 'lt':
|
|
357
|
+
case '<':
|
|
358
|
+
test = (got < want);
|
|
359
|
+
break;
|
|
360
|
+
case '>':
|
|
361
|
+
case 'gt':
|
|
362
|
+
test = (got > want);
|
|
363
|
+
break;
|
|
364
|
+
case 'le':
|
|
365
|
+
case 'lte':
|
|
366
|
+
case '<=':
|
|
367
|
+
test = (got <= want);
|
|
368
|
+
break;
|
|
369
|
+
case 'ge':
|
|
370
|
+
case 'gte':
|
|
371
|
+
case '>=':
|
|
372
|
+
test = (got >= want);
|
|
373
|
+
break;
|
|
374
|
+
default:
|
|
375
|
+
test = false;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
let details = null;
|
|
379
|
+
if (!test)
|
|
380
|
+
{ // The test failed, add the deets.
|
|
381
|
+
details =
|
|
382
|
+
{
|
|
383
|
+
got,
|
|
384
|
+
wanted: want,
|
|
385
|
+
stringify,
|
|
386
|
+
comparitor: comp,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return this.ok(test, desc, null, details);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* See if two values are equal.
|
|
395
|
+
*
|
|
396
|
+
* The same as using `cmp()` with the `===` comparitor.
|
|
397
|
+
*
|
|
398
|
+
* @param {*} got
|
|
399
|
+
* @param {*} want
|
|
400
|
+
* @param {string} [desc]
|
|
401
|
+
* @param {boolean} [stringify=true]
|
|
402
|
+
* @returns {Log}
|
|
403
|
+
*/
|
|
404
|
+
is (got, want, desc, stringify=true)
|
|
405
|
+
{
|
|
406
|
+
return this.cmp(got, want, '===', desc, stringify);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* See if two values are NOT equal.
|
|
411
|
+
*
|
|
412
|
+
* The same as using `cmp()` with the `!==` comparitor.
|
|
413
|
+
*
|
|
414
|
+
* @param {*} got - The result value from the test function.
|
|
415
|
+
* @param {*} want - The value we expected from the test function.
|
|
416
|
+
* @param {string} [desc]
|
|
417
|
+
* @param {boolean} [stringify=true]
|
|
418
|
+
* @returns {Log}
|
|
419
|
+
*/
|
|
420
|
+
isnt (got, want, desc, stringify=true)
|
|
421
|
+
{
|
|
422
|
+
return this.cmp(got, want, '!==', desc, stringify);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* See if a value is of a certain type.
|
|
427
|
+
*
|
|
428
|
+
* @param {*} got
|
|
429
|
+
* @param {Array} wants - Type names or constructor functions.
|
|
430
|
+
*
|
|
431
|
+
* Uses `@lumjs/core/types.isa()` to perform the test.
|
|
432
|
+
*
|
|
433
|
+
* @param {string} [desc]
|
|
434
|
+
* @param {boolean} [stringify=true]
|
|
435
|
+
* @returns {Log}
|
|
436
|
+
*/
|
|
437
|
+
isa (got, wants, desc, stringify=true, not=false)
|
|
438
|
+
{
|
|
439
|
+
if (!isArray(wants))
|
|
440
|
+
{
|
|
441
|
+
wants = [wants];
|
|
442
|
+
}
|
|
443
|
+
let res = types.isa(got, ...wants);
|
|
444
|
+
if (not)
|
|
445
|
+
{ // Inverse the result.
|
|
446
|
+
res = !res;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
let details = null;
|
|
450
|
+
if (!res)
|
|
451
|
+
{ // The test failed, add the deets.
|
|
452
|
+
details =
|
|
453
|
+
{
|
|
454
|
+
got,
|
|
455
|
+
wanted: wants,
|
|
456
|
+
stringify,
|
|
457
|
+
comparitor: not ? 'nota()' : 'isa()',
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return this.ok(res, desc, null, details);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* See if a value is NOT of a certain type.
|
|
466
|
+
*
|
|
467
|
+
* Just inverses the results of `isa()`.
|
|
468
|
+
*
|
|
469
|
+
* @param {*} got
|
|
470
|
+
* @param {Array} wants - Type names of constructor functions.
|
|
471
|
+
* @param {string} [desc]
|
|
472
|
+
* @param {boolean} [stringify=true]
|
|
473
|
+
* @returns {Log}
|
|
474
|
+
*/
|
|
475
|
+
nota (got, wants, desc, stringify=true)
|
|
476
|
+
{
|
|
477
|
+
return this.isa(got, wants, desc, stringify, true);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* An `is()` test, but encode both values as JSON first.
|
|
482
|
+
*
|
|
483
|
+
* Actually uses `core.types.stringify()` so it supports more
|
|
484
|
+
* data types than standard JSON, and can stringify functions,
|
|
485
|
+
* symbols, and several extended object types.
|
|
486
|
+
*
|
|
487
|
+
* @param {*} got
|
|
488
|
+
* @param {*} want
|
|
489
|
+
* @param {string} desc
|
|
490
|
+
* @returns
|
|
491
|
+
*/
|
|
492
|
+
isJSON (got, want, desc)
|
|
493
|
+
{
|
|
494
|
+
got = types.stringify(got);
|
|
495
|
+
want = types.stringify(want);
|
|
496
|
+
return this.is(got, want, desc, false);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* An `isnt()` test, but encode both values as JSON first.
|
|
501
|
+
*
|
|
502
|
+
* Like `isJSON()` this uses `core.types.stringify()`.
|
|
503
|
+
*
|
|
504
|
+
* @param {*} got
|
|
505
|
+
* @param {*} want
|
|
506
|
+
* @param {string} desc
|
|
507
|
+
* @returns
|
|
508
|
+
*/
|
|
509
|
+
isntJSON (got, want, desc)
|
|
510
|
+
{
|
|
511
|
+
got = types.stringify(got);
|
|
512
|
+
want = types.stringify(want);
|
|
513
|
+
return this.isnt(got, want, desc, false);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Skip a test.
|
|
518
|
+
*
|
|
519
|
+
* @param {?string} reason - Why the test was skipped.
|
|
520
|
+
* @param {string} desc
|
|
521
|
+
* @returns {Log}
|
|
522
|
+
*/
|
|
523
|
+
skip (reason, desc)
|
|
524
|
+
{
|
|
525
|
+
var log = this.ok(true, desc);
|
|
526
|
+
log.skipped = true;
|
|
527
|
+
if (typeof reason === S)
|
|
528
|
+
log.skippedReason = reason;
|
|
529
|
+
this.skipped++;
|
|
530
|
+
return log;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Add diagnostics info directly to our test logs.
|
|
535
|
+
*
|
|
536
|
+
* @param {*} msg - The info to add.
|
|
537
|
+
* If this is a `string` it will be displayed as a comment as is.
|
|
538
|
+
* If it is anything else, it will be encoded as JSON first.
|
|
539
|
+
*
|
|
540
|
+
*/
|
|
541
|
+
diag (msg)
|
|
542
|
+
{
|
|
543
|
+
this.log.push(msg);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Run an assortment of tests using a map.
|
|
548
|
+
*
|
|
549
|
+
* The *current* test method defaults to `ok`.
|
|
550
|
+
*
|
|
551
|
+
* @param {...any} tests - The tests we're running.
|
|
552
|
+
*
|
|
553
|
+
* If this is a `string`, and is the name of one of the standard testing
|
|
554
|
+
* methods in this class, it will be set as the *current* test method.
|
|
555
|
+
*
|
|
556
|
+
* If this is a `function`, it will be set as the *current* test method.
|
|
557
|
+
* Function test methods are passed to `call()` with the test parameters.
|
|
558
|
+
*
|
|
559
|
+
* 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 *first*
|
|
561
|
+
* parameter is **always** the `desc`, and any subsequent parameters will be
|
|
562
|
+
* passed to the custom `function` call.
|
|
563
|
+
*
|
|
564
|
+
* Any value other than one of those will throw a `TypeError`.
|
|
565
|
+
*
|
|
566
|
+
* @returns {Log[]} A `Log` item for each test that was ran.
|
|
567
|
+
*/
|
|
568
|
+
run(...tests)
|
|
569
|
+
{
|
|
570
|
+
const logs = [];
|
|
571
|
+
let current = 'ok';
|
|
572
|
+
|
|
573
|
+
for (const test of tests)
|
|
574
|
+
{
|
|
575
|
+
const tt = typeof test;
|
|
576
|
+
if (tt === F || (tt === S && TEST_METHODS.includes(test)))
|
|
577
|
+
{ // Set the current test.
|
|
578
|
+
current = test;
|
|
579
|
+
}
|
|
580
|
+
else if (isArray(test))
|
|
581
|
+
{ // A set of test parameters.
|
|
582
|
+
let log;
|
|
583
|
+
if (typeof current === F)
|
|
584
|
+
{ // A custom test function is in use.
|
|
585
|
+
log = this.call(current, ...test);
|
|
586
|
+
}
|
|
587
|
+
else
|
|
588
|
+
{ // A standard test is in use.
|
|
589
|
+
log = this[current](...test);
|
|
590
|
+
}
|
|
591
|
+
logs.push(log);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return logs;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Return TAP formatted output for all the tests.
|
|
600
|
+
*
|
|
601
|
+
* @returns {string} The test logs, in TAP format.
|
|
602
|
+
*/
|
|
603
|
+
tap ()
|
|
604
|
+
{
|
|
605
|
+
var out = '';
|
|
606
|
+
if (this.planned > 0)
|
|
607
|
+
{
|
|
608
|
+
out += '1..'+this.planned+"\n";
|
|
609
|
+
}
|
|
610
|
+
var t = 1;
|
|
611
|
+
for (var i = 0; i < this.log.length; i++)
|
|
612
|
+
{
|
|
613
|
+
var log = this.log[i];
|
|
614
|
+
if (log instanceof Log)
|
|
615
|
+
{
|
|
616
|
+
out += log.tap(t++);
|
|
617
|
+
}
|
|
618
|
+
else
|
|
619
|
+
{ // A comment.
|
|
620
|
+
out += '# ' + (typeof log === S ? log : types.stringify(log)) + "\n";
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (this.skipped)
|
|
624
|
+
{
|
|
625
|
+
out += '# Skipped '+this.skipped+" tests\n";
|
|
626
|
+
}
|
|
627
|
+
if (this.failed)
|
|
628
|
+
{
|
|
629
|
+
out += '# Failed '+this.failed+(this.failed>1?' tests':' test');
|
|
630
|
+
if (this.planned)
|
|
631
|
+
out += ' out of '+this.planned;
|
|
632
|
+
out += "\n";
|
|
633
|
+
}
|
|
634
|
+
var ran = t-1;
|
|
635
|
+
if (this.planned > 0 && this.planned != ran)
|
|
636
|
+
{
|
|
637
|
+
out += '# Looks like you planned '+this.planned+' but ran '+ran+" tests\n";
|
|
638
|
+
}
|
|
639
|
+
return out;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Send the TAP output to the `console`.
|
|
644
|
+
*/
|
|
645
|
+
output ()
|
|
646
|
+
{
|
|
647
|
+
console.log(this.tap());
|
|
648
|
+
return this;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
} // class Test
|
|
652
|
+
|
|
653
|
+
// Should never need this, but...
|
|
654
|
+
def(Test, 'Log', Log);
|
|
655
|
+
|
|
656
|
+
// Probably don't need this either, but...
|
|
657
|
+
def(Test, '$call', $call);
|
|
658
|
+
|
|
659
|
+
// Methods we're exporting for the 'functional' API.
|
|
660
|
+
def(Test, '$METHODS',
|
|
661
|
+
{
|
|
662
|
+
test: TEST_METHODS,
|
|
663
|
+
meta: META_METHODS,
|
|
664
|
+
get all()
|
|
665
|
+
{
|
|
666
|
+
const list = [];
|
|
667
|
+
for (const name in this)
|
|
668
|
+
{
|
|
669
|
+
if (name === 'all') continue;
|
|
670
|
+
const prop = this[name];
|
|
671
|
+
if (isArray(prop))
|
|
672
|
+
{
|
|
673
|
+
list.push(...prop);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return list;
|
|
677
|
+
},
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
// Export the class
|
|
681
|
+
module.exports = Test;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumjs/tests",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"main": "index.js",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"main": "lib/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository":
|
|
7
7
|
{
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
"url": "https://github.com/supernovus/lum.tests.js.git"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@lumjs/core": "^1.0.0"
|
|
12
|
+
"@lumjs/core": "^1.0.0-beta.4"
|
|
13
|
+
},
|
|
14
|
+
"scripts":
|
|
15
|
+
{
|
|
16
|
+
"-TODO-1": "When Harness and bin/lumtest are done, use that for 'test'",
|
|
17
|
+
"test": "prove -e node --ext js ./test"
|
|
13
18
|
}
|
|
14
19
|
}
|