'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); };