577 lines
15 KiB
JavaScript
577 lines
15 KiB
JavaScript
'use strict';
|
|
|
|
Object.defineProperty(exports, '__esModule', {
|
|
value: true
|
|
});
|
|
|
|
var _slicedToArray = (function() {
|
|
function sliceIterator(arr, i) {
|
|
var _arr = [];
|
|
var _n = true;
|
|
var _d = false;
|
|
var _e = undefined;
|
|
try {
|
|
for (
|
|
var _i = arr[Symbol.iterator](), _s;
|
|
!(_n = (_s = _i.next()).done);
|
|
_n = true
|
|
) {
|
|
_arr.push(_s.value);
|
|
if (i && _arr.length === i) break;
|
|
}
|
|
} catch (err) {
|
|
_d = true;
|
|
_e = err;
|
|
} finally {
|
|
try {
|
|
if (!_n && _i['return']) _i['return']();
|
|
} finally {
|
|
if (_d) throw _e;
|
|
}
|
|
}
|
|
return _arr;
|
|
}
|
|
return function(arr, i) {
|
|
if (Array.isArray(arr)) {
|
|
return arr;
|
|
} else if (Symbol.iterator in Object(arr)) {
|
|
return sliceIterator(arr, i);
|
|
} else {
|
|
throw new TypeError(
|
|
'Invalid attempt to destructure non-iterable instance'
|
|
);
|
|
}
|
|
};
|
|
})();
|
|
|
|
var _chalk;
|
|
|
|
function _load_chalk() {
|
|
return (_chalk = _interopRequireDefault(require('chalk')));
|
|
}
|
|
|
|
var _jestMessageUtil;
|
|
|
|
function _load_jestMessageUtil() {
|
|
return (_jestMessageUtil = require('jest-message-util'));
|
|
}
|
|
|
|
var _testResultHelpers;
|
|
|
|
function _load_testResultHelpers() {
|
|
return (_testResultHelpers = require('./testResultHelpers'));
|
|
}
|
|
|
|
var _coverage_reporter;
|
|
|
|
function _load_coverage_reporter() {
|
|
return (_coverage_reporter = _interopRequireDefault(
|
|
require('./reporters/coverage_reporter')
|
|
));
|
|
}
|
|
|
|
var _default_reporter;
|
|
|
|
function _load_default_reporter() {
|
|
return (_default_reporter = _interopRequireDefault(
|
|
require('./reporters/default_reporter')
|
|
));
|
|
}
|
|
|
|
var _exit;
|
|
|
|
function _load_exit() {
|
|
return (_exit = _interopRequireDefault(require('exit')));
|
|
}
|
|
|
|
var _notify_reporter;
|
|
|
|
function _load_notify_reporter() {
|
|
return (_notify_reporter = _interopRequireDefault(
|
|
require('./reporters/notify_reporter')
|
|
));
|
|
}
|
|
|
|
var _ReporterDispatcher;
|
|
|
|
function _load_ReporterDispatcher() {
|
|
return (_ReporterDispatcher = _interopRequireDefault(
|
|
require('./ReporterDispatcher')
|
|
));
|
|
}
|
|
|
|
var _jestSnapshot;
|
|
|
|
function _load_jestSnapshot() {
|
|
return (_jestSnapshot = _interopRequireDefault(require('jest-snapshot')));
|
|
}
|
|
|
|
var _summary_reporter;
|
|
|
|
function _load_summary_reporter() {
|
|
return (_summary_reporter = _interopRequireDefault(
|
|
require('./reporters/summary_reporter')
|
|
));
|
|
}
|
|
|
|
var _jestRunner;
|
|
|
|
function _load_jestRunner() {
|
|
return (_jestRunner = _interopRequireDefault(require('jest-runner')));
|
|
}
|
|
|
|
var _TestWatcher;
|
|
|
|
function _load_TestWatcher() {
|
|
return (_TestWatcher = _interopRequireDefault(require('./TestWatcher')));
|
|
}
|
|
|
|
var _verbose_reporter;
|
|
|
|
function _load_verbose_reporter() {
|
|
return (_verbose_reporter = _interopRequireDefault(
|
|
require('./reporters/verbose_reporter')
|
|
));
|
|
}
|
|
|
|
function _interopRequireDefault(obj) {
|
|
return obj && obj.__esModule ? obj : {default: obj};
|
|
}
|
|
|
|
function _asyncToGenerator(fn) {
|
|
return function() {
|
|
var gen = fn.apply(this, arguments);
|
|
return new Promise(function(resolve, reject) {
|
|
function step(key, arg) {
|
|
try {
|
|
var info = gen[key](arg);
|
|
var value = info.value;
|
|
} catch (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
if (info.done) {
|
|
resolve(value);
|
|
} else {
|
|
return Promise.resolve(value).then(
|
|
function(value) {
|
|
step('next', value);
|
|
},
|
|
function(err) {
|
|
step('throw', err);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
return step('next');
|
|
});
|
|
};
|
|
}
|
|
/**
|
|
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*
|
|
*/
|
|
|
|
const SLOW_TEST_TIME = 1000;
|
|
|
|
// The default jest-runner is required because it is the default test runner
|
|
// and required implicitly through the `runner` ProjectConfig option.
|
|
(_jestRunner || _load_jestRunner()).default;
|
|
|
|
class TestScheduler {
|
|
constructor(globalConfig, options, context) {
|
|
this._dispatcher = new (
|
|
_ReporterDispatcher || _load_ReporterDispatcher()
|
|
).default();
|
|
this._globalConfig = globalConfig;
|
|
this._options = options;
|
|
this._context = context;
|
|
this._setupReporters();
|
|
}
|
|
|
|
addReporter(reporter) {
|
|
this._dispatcher.register(reporter);
|
|
}
|
|
|
|
removeReporter(ReporterClass) {
|
|
this._dispatcher.unregister(ReporterClass);
|
|
}
|
|
|
|
scheduleTests(tests, watcher) {
|
|
var _this = this;
|
|
|
|
return _asyncToGenerator(function*() {
|
|
const onStart = _this._dispatcher.onTestStart.bind(_this._dispatcher);
|
|
const timings = [];
|
|
const contexts = new Set();
|
|
tests.forEach(function(test) {
|
|
contexts.add(test.context);
|
|
if (test.duration) {
|
|
timings.push(test.duration);
|
|
}
|
|
});
|
|
|
|
const aggregatedResults = createAggregatedResults(tests.length);
|
|
const estimatedTime = Math.ceil(
|
|
getEstimatedTime(timings, _this._globalConfig.maxWorkers) / 1000
|
|
);
|
|
|
|
// Run in band if we only have one test or one worker available, unless we
|
|
// are using the watch mode, in which case the TTY has to be responsive and
|
|
// we cannot schedule anything in the main thread. Same logic applies to
|
|
// watchAll.
|
|
//
|
|
// If we are confident from previous runs that the tests will finish
|
|
// quickly we also run in band to reduce the overhead of spawning workers.
|
|
const areFastTests = timings.every(function(timing) {
|
|
return timing < SLOW_TEST_TIME;
|
|
});
|
|
|
|
const runInBandWatch = tests.length <= 1 && areFastTests;
|
|
const runInBandNonWatch =
|
|
_this._globalConfig.maxWorkers <= 1 ||
|
|
tests.length <= 1 ||
|
|
(tests.length <= 20 && timings.length > 0 && areFastTests);
|
|
|
|
const runInBand =
|
|
_this._globalConfig.watch || _this._globalConfig.watchAll
|
|
? runInBandWatch
|
|
: runInBandNonWatch;
|
|
|
|
const onResult = (() => {
|
|
var _ref = _asyncToGenerator(function*(test, testResult) {
|
|
if (watcher.isInterrupted()) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
if (testResult.testResults.length === 0) {
|
|
const message = 'Your test suite must contain at least one test.';
|
|
|
|
return onFailure(test, {
|
|
message: message,
|
|
stack: new Error(message).stack
|
|
});
|
|
}
|
|
|
|
// Throws when the context is leaked after executing a test.
|
|
if (testResult.leaks) {
|
|
const message =
|
|
(_chalk || _load_chalk()).default.red.bold(
|
|
'EXPERIMENTAL FEATURE!\n'
|
|
) +
|
|
'Your test suite is leaking memory. Please ensure all references are cleaned.\n' +
|
|
'\n' +
|
|
'There is a number of things that can leak memory:\n' +
|
|
' - Async operations that have not finished (e.g. fs.readFile).\n' +
|
|
' - Timers not properly mocked (e.g. setInterval, setTimeout).\n' +
|
|
' - Keeping references to the global scope.';
|
|
|
|
return onFailure(test, {
|
|
message: message,
|
|
stack: new Error(message).stack
|
|
});
|
|
}
|
|
|
|
(0,
|
|
(_testResultHelpers || _load_testResultHelpers())
|
|
.addResult)(aggregatedResults, testResult);
|
|
yield _this._dispatcher.onTestResult(
|
|
test,
|
|
testResult,
|
|
aggregatedResults
|
|
);
|
|
return _this._bailIfNeeded(contexts, aggregatedResults, watcher);
|
|
});
|
|
|
|
return function onResult(_x, _x2) {
|
|
return _ref.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
const onFailure = (() => {
|
|
var _ref2 = _asyncToGenerator(function*(test, error) {
|
|
if (watcher.isInterrupted()) {
|
|
return;
|
|
}
|
|
const testResult = (0,
|
|
(_testResultHelpers || _load_testResultHelpers())
|
|
.buildFailureTestResult)(test.path, error);
|
|
testResult.failureMessage = (0,
|
|
(_jestMessageUtil || _load_jestMessageUtil()).formatExecError)(
|
|
testResult.testExecError,
|
|
test.context.config,
|
|
_this._globalConfig,
|
|
test.path
|
|
);
|
|
(0,
|
|
(_testResultHelpers || _load_testResultHelpers())
|
|
.addResult)(aggregatedResults, testResult);
|
|
yield _this._dispatcher.onTestResult(
|
|
test,
|
|
testResult,
|
|
aggregatedResults
|
|
);
|
|
});
|
|
|
|
return function onFailure(_x3, _x4) {
|
|
return _ref2.apply(this, arguments);
|
|
};
|
|
})();
|
|
|
|
const updateSnapshotState = function() {
|
|
contexts.forEach(function(context) {
|
|
const status = (
|
|
_jestSnapshot || _load_jestSnapshot()
|
|
).default.cleanup(
|
|
context.hasteFS,
|
|
_this._globalConfig.updateSnapshot
|
|
);
|
|
|
|
aggregatedResults.snapshot.filesRemoved += status.filesRemoved;
|
|
});
|
|
const updateAll = _this._globalConfig.updateSnapshot === 'all';
|
|
aggregatedResults.snapshot.didUpdate = updateAll;
|
|
aggregatedResults.snapshot.failure = !!(
|
|
!updateAll &&
|
|
(aggregatedResults.snapshot.unchecked ||
|
|
aggregatedResults.snapshot.unmatched ||
|
|
aggregatedResults.snapshot.filesRemoved)
|
|
);
|
|
};
|
|
|
|
yield _this._dispatcher.onRunStart(aggregatedResults, {
|
|
estimatedTime: estimatedTime,
|
|
showStatus: !runInBand
|
|
});
|
|
|
|
const testRunners = Object.create(null);
|
|
contexts.forEach(function(_ref3) {
|
|
let config = _ref3.config;
|
|
|
|
if (!testRunners[config.runner]) {
|
|
// $FlowFixMe
|
|
testRunners[config.runner] = new (require(config.runner))(
|
|
_this._globalConfig
|
|
);
|
|
}
|
|
});
|
|
|
|
const testsByRunner = _this._partitionTests(testRunners, tests);
|
|
|
|
if (testsByRunner) {
|
|
try {
|
|
for (const runner of Object.keys(testRunners)) {
|
|
yield testRunners[runner].runTests(
|
|
testsByRunner[runner],
|
|
watcher,
|
|
onStart,
|
|
onResult,
|
|
onFailure,
|
|
{
|
|
serial: runInBand || Boolean(testRunners[runner].isSerial)
|
|
}
|
|
);
|
|
}
|
|
} catch (error) {
|
|
if (!watcher.isInterrupted()) {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
updateSnapshotState();
|
|
aggregatedResults.wasInterrupted = watcher.isInterrupted();
|
|
yield _this._dispatcher.onRunComplete(contexts, aggregatedResults);
|
|
|
|
const anyTestFailures = !(
|
|
aggregatedResults.numFailedTests === 0 &&
|
|
aggregatedResults.numRuntimeErrorTestSuites === 0
|
|
);
|
|
const anyReporterErrors = _this._dispatcher.hasErrors();
|
|
|
|
aggregatedResults.success = !(
|
|
anyTestFailures ||
|
|
aggregatedResults.snapshot.failure ||
|
|
anyReporterErrors
|
|
);
|
|
|
|
return aggregatedResults;
|
|
})();
|
|
}
|
|
|
|
_partitionTests(testRunners, tests) {
|
|
if (Object.keys(testRunners).length > 1) {
|
|
return tests.reduce((testRuns, test) => {
|
|
const runner = test.context.config.runner;
|
|
if (!testRuns[runner]) {
|
|
testRuns[runner] = [];
|
|
}
|
|
testRuns[runner].push(test);
|
|
return testRuns;
|
|
}, Object.create(null));
|
|
} else if (tests.length > 0 && tests[0] != null) {
|
|
// If there is only one runner, don't partition the tests.
|
|
return Object.assign(Object.create(null), {
|
|
[tests[0].context.config.runner]: tests
|
|
});
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
_shouldAddDefaultReporters(reporters) {
|
|
return (
|
|
!reporters ||
|
|
!!reporters.find(
|
|
reporter => this._getReporterProps(reporter).path === 'default'
|
|
)
|
|
);
|
|
}
|
|
|
|
_setupReporters() {
|
|
var _globalConfig = this._globalConfig;
|
|
const collectCoverage = _globalConfig.collectCoverage,
|
|
notify = _globalConfig.notify,
|
|
reporters = _globalConfig.reporters;
|
|
|
|
const isDefault = this._shouldAddDefaultReporters(reporters);
|
|
|
|
if (isDefault) {
|
|
this._setupDefaultReporters(collectCoverage);
|
|
}
|
|
|
|
if (!isDefault && collectCoverage) {
|
|
this.addReporter(
|
|
new (_coverage_reporter || _load_coverage_reporter()).default(
|
|
this._globalConfig
|
|
)
|
|
);
|
|
}
|
|
|
|
if (notify) {
|
|
this.addReporter(
|
|
new (_notify_reporter || _load_notify_reporter()).default(
|
|
this._globalConfig,
|
|
this._options.startRun,
|
|
this._context
|
|
)
|
|
);
|
|
}
|
|
|
|
if (reporters && Array.isArray(reporters)) {
|
|
this._addCustomReporters(reporters);
|
|
}
|
|
}
|
|
|
|
_setupDefaultReporters(collectCoverage) {
|
|
this.addReporter(
|
|
this._globalConfig.verbose
|
|
? new (_verbose_reporter || _load_verbose_reporter()).default(
|
|
this._globalConfig
|
|
)
|
|
: new (_default_reporter || _load_default_reporter()).default(
|
|
this._globalConfig
|
|
)
|
|
);
|
|
|
|
if (collectCoverage) {
|
|
this.addReporter(
|
|
new (_coverage_reporter || _load_coverage_reporter()).default(
|
|
this._globalConfig
|
|
)
|
|
);
|
|
}
|
|
|
|
this.addReporter(
|
|
new (_summary_reporter || _load_summary_reporter()).default(
|
|
this._globalConfig
|
|
)
|
|
);
|
|
}
|
|
|
|
_addCustomReporters(reporters) {
|
|
reporters.forEach((reporter, index) => {
|
|
var _getReporterProps = this._getReporterProps(reporter);
|
|
|
|
const options = _getReporterProps.options,
|
|
path = _getReporterProps.path;
|
|
|
|
if (path === 'default') return;
|
|
|
|
try {
|
|
// $FlowFixMe
|
|
const Reporter = require(path);
|
|
this.addReporter(new Reporter(this._globalConfig, options));
|
|
} catch (error) {
|
|
throw new Error(
|
|
'An error occurred while adding the reporter at path "' +
|
|
path +
|
|
'".' +
|
|
error.message
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get properties of a reporter in an object
|
|
* to make dealing with them less painful.
|
|
*/
|
|
_getReporterProps(reporter) {
|
|
if (typeof reporter === 'string') {
|
|
return {options: this._options, path: reporter};
|
|
} else if (Array.isArray(reporter)) {
|
|
var _reporter = _slicedToArray(reporter, 2);
|
|
|
|
const path = _reporter[0],
|
|
options = _reporter[1];
|
|
|
|
return {options: options, path: path};
|
|
}
|
|
|
|
throw new Error('Reporter should be either a string or an array');
|
|
}
|
|
|
|
_bailIfNeeded(contexts, aggregatedResults, watcher) {
|
|
if (this._globalConfig.bail && aggregatedResults.numFailedTests !== 0) {
|
|
if (watcher.isWatchMode()) {
|
|
watcher.setState({interrupted: true});
|
|
} else {
|
|
const failureExit = () => (0, (_exit || _load_exit()).default)(1);
|
|
|
|
return this._dispatcher
|
|
.onRunComplete(contexts, aggregatedResults)
|
|
.then(failureExit)
|
|
.catch(failureExit);
|
|
}
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
}
|
|
|
|
exports.default = TestScheduler;
|
|
const createAggregatedResults = numTotalTestSuites => {
|
|
const result = (0,
|
|
(_testResultHelpers || _load_testResultHelpers())
|
|
.makeEmptyAggregatedTestResult)();
|
|
result.numTotalTestSuites = numTotalTestSuites;
|
|
result.startTime = Date.now();
|
|
result.success = false;
|
|
return result;
|
|
};
|
|
|
|
const getEstimatedTime = (timings, workers) => {
|
|
if (!timings.length) {
|
|
return 0;
|
|
}
|
|
|
|
const max = Math.max.apply(null, timings);
|
|
return timings.length <= workers
|
|
? max
|
|
: Math.max(timings.reduce((sum, time) => sum + time) / workers, max);
|
|
};
|