mocha 6.2.1 → 7.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 +74 -0
- package/README.md +26 -26
- package/bin/mocha +12 -43
- package/lib/cli/node-flags.js +5 -2
- package/lib/cli/one-and-dones.js +2 -2
- package/lib/cli/options.js +4 -34
- package/lib/cli/run-helpers.js +4 -3
- package/lib/cli/run-option-metadata.js +6 -5
- package/lib/cli/run.js +24 -11
- package/lib/cli/watch-run.js +116 -31
- package/lib/mocha.js +137 -102
- package/lib/mocharc.json +2 -1
- package/lib/reporters/base.js +33 -8
- package/lib/reporters/html.js +2 -2
- package/lib/reporters/xunit.js +3 -3
- package/lib/runnable.js +16 -17
- package/lib/runner.js +76 -45
- package/lib/utils.js +0 -74
- package/mocha.js +271 -254
- package/package.json +31 -6
- package/bin/options.js +0 -10
package/lib/runnable.js
CHANGED
|
@@ -135,7 +135,8 @@ Runnable.prototype.enableTimeouts = function(enabled) {
|
|
|
135
135
|
* @public
|
|
136
136
|
*/
|
|
137
137
|
Runnable.prototype.skip = function() {
|
|
138
|
-
|
|
138
|
+
this.pending = true;
|
|
139
|
+
throw new Pending('sync skip; aborting execution');
|
|
139
140
|
};
|
|
140
141
|
|
|
141
142
|
/**
|
|
@@ -343,34 +344,27 @@ Runnable.prototype.run = function(fn) {
|
|
|
343
344
|
|
|
344
345
|
// allows skip() to be used in an explicit async context
|
|
345
346
|
this.skip = function asyncSkip() {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
//
|
|
349
|
-
// the failure.
|
|
347
|
+
this.pending = true;
|
|
348
|
+
done();
|
|
349
|
+
// halt execution, the uncaught handler will ignore the failure.
|
|
350
350
|
throw new Pending('async skip; aborting execution');
|
|
351
351
|
};
|
|
352
352
|
|
|
353
|
-
if (this.allowUncaught) {
|
|
354
|
-
return callFnAsync(this.fn);
|
|
355
|
-
}
|
|
356
353
|
try {
|
|
357
354
|
callFnAsync(this.fn);
|
|
358
355
|
} catch (err) {
|
|
356
|
+
// handles async runnables which actually run synchronously
|
|
359
357
|
emitted = true;
|
|
358
|
+
if (err instanceof Pending) {
|
|
359
|
+
return; // done() is already called in this.skip()
|
|
360
|
+
} else if (this.allowUncaught) {
|
|
361
|
+
throw err;
|
|
362
|
+
}
|
|
360
363
|
done(Runnable.toValueOrError(err));
|
|
361
364
|
}
|
|
362
365
|
return;
|
|
363
366
|
}
|
|
364
367
|
|
|
365
|
-
if (this.allowUncaught) {
|
|
366
|
-
if (this.isPending()) {
|
|
367
|
-
done();
|
|
368
|
-
} else {
|
|
369
|
-
callFn(this.fn);
|
|
370
|
-
}
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
368
|
// sync or promise-returning
|
|
375
369
|
try {
|
|
376
370
|
if (this.isPending()) {
|
|
@@ -380,6 +374,11 @@ Runnable.prototype.run = function(fn) {
|
|
|
380
374
|
}
|
|
381
375
|
} catch (err) {
|
|
382
376
|
emitted = true;
|
|
377
|
+
if (err instanceof Pending) {
|
|
378
|
+
return done();
|
|
379
|
+
} else if (this.allowUncaught) {
|
|
380
|
+
throw err;
|
|
381
|
+
}
|
|
383
382
|
done(Runnable.toValueOrError(err));
|
|
384
383
|
}
|
|
385
384
|
|
package/lib/runner.js
CHANGED
|
@@ -24,8 +24,9 @@ var sQuote = utils.sQuote;
|
|
|
24
24
|
var stackFilter = utils.stackTraceFilter();
|
|
25
25
|
var stringify = utils.stringify;
|
|
26
26
|
var type = utils.type;
|
|
27
|
-
var
|
|
28
|
-
|
|
27
|
+
var errors = require('./errors');
|
|
28
|
+
var createInvalidExceptionError = errors.createInvalidExceptionError;
|
|
29
|
+
var createUnsupportedError = errors.createUnsupportedError;
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Non-enumerable globals.
|
|
@@ -244,7 +245,7 @@ Runner.prototype.globals = function(arr) {
|
|
|
244
245
|
* @private
|
|
245
246
|
*/
|
|
246
247
|
Runner.prototype.checkGlobals = function(test) {
|
|
247
|
-
if (this.
|
|
248
|
+
if (!this.checkLeaks) {
|
|
248
249
|
return;
|
|
249
250
|
}
|
|
250
251
|
var ok = this._globals;
|
|
@@ -315,8 +316,7 @@ Runner.prototype.fail = function(test, err) {
|
|
|
315
316
|
* - Failed `before each` hook skips remaining tests in a
|
|
316
317
|
* suite and jumps to corresponding `after each` hook,
|
|
317
318
|
* which is run only once
|
|
318
|
-
* - Failed `after` hook does not alter
|
|
319
|
-
* execution order
|
|
319
|
+
* - Failed `after` hook does not alter execution order
|
|
320
320
|
* - Failed `after each` hook skips remaining tests in a
|
|
321
321
|
* suite and subsuites, but executes other `after each`
|
|
322
322
|
* hooks
|
|
@@ -386,34 +386,37 @@ Runner.prototype.hook = function(name, fn) {
|
|
|
386
386
|
if (testError) {
|
|
387
387
|
self.fail(self.test, testError);
|
|
388
388
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
);
|
|
389
|
+
// conditional skip
|
|
390
|
+
if (hook.pending) {
|
|
391
|
+
if (name === HOOK_TYPE_AFTER_EACH) {
|
|
392
|
+
// TODO define and implement use case
|
|
393
|
+
if (self.test) {
|
|
394
|
+
self.test.pending = true;
|
|
396
395
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
} else {
|
|
402
|
-
suite.tests.forEach(function(test) {
|
|
403
|
-
test.pending = true;
|
|
404
|
-
});
|
|
405
|
-
suite.suites.forEach(function(suite) {
|
|
406
|
-
suite.pending = true;
|
|
407
|
-
});
|
|
408
|
-
// a pending hook won't be executed twice.
|
|
409
|
-
hook.pending = true;
|
|
396
|
+
} else if (name === HOOK_TYPE_BEFORE_EACH) {
|
|
397
|
+
if (self.test) {
|
|
398
|
+
self.test.pending = true;
|
|
410
399
|
}
|
|
400
|
+
self.emit(constants.EVENT_HOOK_END, hook);
|
|
401
|
+
hook.pending = false; // activates hook for next test
|
|
402
|
+
return fn(new Error('abort hookDown'));
|
|
403
|
+
} else if (name === HOOK_TYPE_BEFORE_ALL) {
|
|
404
|
+
suite.tests.forEach(function(test) {
|
|
405
|
+
test.pending = true;
|
|
406
|
+
});
|
|
407
|
+
suite.suites.forEach(function(suite) {
|
|
408
|
+
suite.pending = true;
|
|
409
|
+
});
|
|
411
410
|
} else {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
return fn(
|
|
411
|
+
hook.pending = false;
|
|
412
|
+
var errForbid = createUnsupportedError('`this.skip` forbidden');
|
|
413
|
+
self.failHook(hook, errForbid);
|
|
414
|
+
return fn(errForbid);
|
|
416
415
|
}
|
|
416
|
+
} else if (err) {
|
|
417
|
+
self.failHook(hook, err);
|
|
418
|
+
// stop executing hooks, notify callee of hook err
|
|
419
|
+
return fn(err);
|
|
417
420
|
}
|
|
418
421
|
self.emit(constants.EVENT_HOOK_END, hook);
|
|
419
422
|
delete hook.ctx.currentTest;
|
|
@@ -525,6 +528,9 @@ Runner.prototype.runTest = function(fn) {
|
|
|
525
528
|
test.asyncOnly = true;
|
|
526
529
|
}
|
|
527
530
|
test.on('error', function(err) {
|
|
531
|
+
if (err instanceof Pending) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
528
534
|
self.fail(test, err);
|
|
529
535
|
});
|
|
530
536
|
if (this.allowUncaught) {
|
|
@@ -620,6 +626,7 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
620
626
|
return;
|
|
621
627
|
}
|
|
622
628
|
|
|
629
|
+
// static skip, no hooks are executed
|
|
623
630
|
if (test.isPending()) {
|
|
624
631
|
if (self.forbidPending) {
|
|
625
632
|
test.isPending = alwaysFalse;
|
|
@@ -635,6 +642,7 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
635
642
|
// execute test and hook(s)
|
|
636
643
|
self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
|
|
637
644
|
self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
|
|
645
|
+
// conditional skip within beforeEach
|
|
638
646
|
if (test.isPending()) {
|
|
639
647
|
if (self.forbidPending) {
|
|
640
648
|
test.isPending = alwaysFalse;
|
|
@@ -644,7 +652,13 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
644
652
|
self.emit(constants.EVENT_TEST_PENDING, test);
|
|
645
653
|
}
|
|
646
654
|
self.emit(constants.EVENT_TEST_END, test);
|
|
647
|
-
|
|
655
|
+
// skip inner afterEach hooks below errSuite level
|
|
656
|
+
var origSuite = self.suite;
|
|
657
|
+
self.suite = errSuite;
|
|
658
|
+
return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) {
|
|
659
|
+
self.suite = origSuite;
|
|
660
|
+
next(e, eSuite);
|
|
661
|
+
});
|
|
648
662
|
}
|
|
649
663
|
if (err) {
|
|
650
664
|
return hookErr(err, errSuite, false);
|
|
@@ -652,14 +666,20 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
652
666
|
self.currentRunnable = self.test;
|
|
653
667
|
self.runTest(function(err) {
|
|
654
668
|
test = self.test;
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if (
|
|
669
|
+
// conditional skip within it
|
|
670
|
+
if (test.pending) {
|
|
671
|
+
if (self.forbidPending) {
|
|
672
|
+
test.isPending = alwaysFalse;
|
|
658
673
|
self.fail(test, new Error('Pending test forbidden'));
|
|
659
|
-
|
|
660
|
-
|
|
674
|
+
delete test.isPending;
|
|
675
|
+
} else {
|
|
661
676
|
self.emit(constants.EVENT_TEST_PENDING, test);
|
|
662
|
-
}
|
|
677
|
+
}
|
|
678
|
+
self.emit(constants.EVENT_TEST_END, test);
|
|
679
|
+
return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
|
|
680
|
+
} else if (err) {
|
|
681
|
+
var retry = test.currentRetry();
|
|
682
|
+
if (retry < test.retries()) {
|
|
663
683
|
var clonedTest = test.clone();
|
|
664
684
|
clonedTest.currentRetry(retry + 1);
|
|
665
685
|
tests.unshift(clonedTest);
|
|
@@ -673,11 +693,6 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
673
693
|
self.fail(test, err);
|
|
674
694
|
}
|
|
675
695
|
self.emit(constants.EVENT_TEST_END, test);
|
|
676
|
-
|
|
677
|
-
if (err instanceof Pending) {
|
|
678
|
-
return next();
|
|
679
|
-
}
|
|
680
|
-
|
|
681
696
|
return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
|
|
682
697
|
}
|
|
683
698
|
|
|
@@ -794,6 +809,10 @@ Runner.prototype.uncaught = function(err) {
|
|
|
794
809
|
if (err instanceof Pending) {
|
|
795
810
|
return;
|
|
796
811
|
}
|
|
812
|
+
if (this.allowUncaught) {
|
|
813
|
+
throw err;
|
|
814
|
+
}
|
|
815
|
+
|
|
797
816
|
if (err) {
|
|
798
817
|
debug('uncaught exception %O', err);
|
|
799
818
|
} else {
|
|
@@ -829,11 +848,17 @@ Runner.prototype.uncaught = function(err) {
|
|
|
829
848
|
|
|
830
849
|
runnable.clearTimeout();
|
|
831
850
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
851
|
+
if (runnable.isFailed()) {
|
|
852
|
+
// Ignore error if already failed
|
|
853
|
+
return;
|
|
854
|
+
} else if (runnable.isPending()) {
|
|
855
|
+
// report 'pending test' retrospectively as failed
|
|
856
|
+
runnable.isPending = alwaysFalse;
|
|
857
|
+
this.fail(runnable, err);
|
|
858
|
+
delete runnable.isPending;
|
|
835
859
|
return;
|
|
836
860
|
}
|
|
861
|
+
|
|
837
862
|
// we cannot recover gracefully if a Runnable has already passed
|
|
838
863
|
// then fails asynchronously
|
|
839
864
|
var alreadyPassed = runnable.isPassed();
|
|
@@ -865,7 +890,7 @@ Runner.prototype.uncaught = function(err) {
|
|
|
865
890
|
}
|
|
866
891
|
|
|
867
892
|
// bail
|
|
868
|
-
this.
|
|
893
|
+
this.abort();
|
|
869
894
|
};
|
|
870
895
|
|
|
871
896
|
/**
|
|
@@ -915,6 +940,12 @@ Runner.prototype.run = function(fn) {
|
|
|
915
940
|
this.on(constants.EVENT_RUN_END, function() {
|
|
916
941
|
debug(constants.EVENT_RUN_END);
|
|
917
942
|
process.removeListener('uncaughtException', uncaught);
|
|
943
|
+
process.on('uncaughtException', function(err) {
|
|
944
|
+
if (err instanceof Pending) {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
throw err;
|
|
948
|
+
});
|
|
918
949
|
fn(self.failures);
|
|
919
950
|
});
|
|
920
951
|
|
package/lib/utils.js
CHANGED
|
@@ -53,80 +53,6 @@ exports.isString = function(obj) {
|
|
|
53
53
|
return typeof obj === 'string';
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
* Watch the given `files` for changes
|
|
58
|
-
* and invoke `fn(file)` on modification.
|
|
59
|
-
*
|
|
60
|
-
* @private
|
|
61
|
-
* @param {Array} files
|
|
62
|
-
* @param {Function} fn
|
|
63
|
-
*/
|
|
64
|
-
exports.watch = function(files, fn) {
|
|
65
|
-
var options = {interval: 100};
|
|
66
|
-
var debug = require('debug')('mocha:watch');
|
|
67
|
-
files.forEach(function(file) {
|
|
68
|
-
debug('file %s', file);
|
|
69
|
-
fs.watchFile(file, options, function(curr, prev) {
|
|
70
|
-
if (prev.mtime < curr.mtime) {
|
|
71
|
-
fn(file);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Predicate to screen `pathname` for further consideration.
|
|
79
|
-
*
|
|
80
|
-
* @description
|
|
81
|
-
* Returns <code>false</code> for pathname referencing:
|
|
82
|
-
* <ul>
|
|
83
|
-
* <li>'npm' package installation directory
|
|
84
|
-
* <li>'git' version control directory
|
|
85
|
-
* </ul>
|
|
86
|
-
*
|
|
87
|
-
* @private
|
|
88
|
-
* @param {string} pathname - File or directory name to screen
|
|
89
|
-
* @return {boolean} whether pathname should be further considered
|
|
90
|
-
* @example
|
|
91
|
-
* ['node_modules', 'test.js'].filter(considerFurther); // => ['test.js']
|
|
92
|
-
*/
|
|
93
|
-
function considerFurther(pathname) {
|
|
94
|
-
var ignore = ['node_modules', '.git'];
|
|
95
|
-
|
|
96
|
-
return !~ignore.indexOf(pathname);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Lookup files in the given `dir`.
|
|
101
|
-
*
|
|
102
|
-
* @description
|
|
103
|
-
* Filenames are returned in _traversal_ order by the OS/filesystem.
|
|
104
|
-
* **Make no assumption that the names will be sorted in any fashion.**
|
|
105
|
-
*
|
|
106
|
-
* @private
|
|
107
|
-
* @param {string} dir
|
|
108
|
-
* @param {string[]} [exts=['js']]
|
|
109
|
-
* @param {Array} [ret=[]]
|
|
110
|
-
* @return {Array}
|
|
111
|
-
*/
|
|
112
|
-
exports.files = function(dir, exts, ret) {
|
|
113
|
-
ret = ret || [];
|
|
114
|
-
exts = exts || ['js'];
|
|
115
|
-
|
|
116
|
-
fs.readdirSync(dir)
|
|
117
|
-
.filter(considerFurther)
|
|
118
|
-
.forEach(function(dirent) {
|
|
119
|
-
var pathname = path.join(dir, dirent);
|
|
120
|
-
if (fs.lstatSync(pathname).isDirectory()) {
|
|
121
|
-
exports.files(pathname, exts, ret);
|
|
122
|
-
} else if (hasMatchingExtname(pathname, exts)) {
|
|
123
|
-
ret.push(pathname);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
return ret;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
56
|
/**
|
|
131
57
|
* Compute a slug from the given `str`.
|
|
132
58
|
*
|