TestScheduler.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. function _chalk() {
  7. const data = _interopRequireDefault(require('chalk'));
  8. _chalk = function _chalk() {
  9. return data;
  10. };
  11. return data;
  12. }
  13. function _jestMessageUtil() {
  14. const data = require('jest-message-util');
  15. _jestMessageUtil = function _jestMessageUtil() {
  16. return data;
  17. };
  18. return data;
  19. }
  20. function _jestSnapshot() {
  21. const data = _interopRequireDefault(require('jest-snapshot'));
  22. _jestSnapshot = function _jestSnapshot() {
  23. return data;
  24. };
  25. return data;
  26. }
  27. function _jestRunner() {
  28. const data = _interopRequireDefault(require('jest-runner'));
  29. _jestRunner = function _jestRunner() {
  30. return data;
  31. };
  32. return data;
  33. }
  34. function _reporters() {
  35. const data = require('@jest/reporters');
  36. _reporters = function _reporters() {
  37. return data;
  38. };
  39. return data;
  40. }
  41. function _exit() {
  42. const data = _interopRequireDefault(require('exit'));
  43. _exit = function _exit() {
  44. return data;
  45. };
  46. return data;
  47. }
  48. function _testResult() {
  49. const data = require('@jest/test-result');
  50. _testResult = function _testResult() {
  51. return data;
  52. };
  53. return data;
  54. }
  55. var _ReporterDispatcher = _interopRequireDefault(
  56. require('./ReporterDispatcher')
  57. );
  58. var _testSchedulerHelper = require('./testSchedulerHelper');
  59. function _interopRequireDefault(obj) {
  60. return obj && obj.__esModule ? obj : {default: obj};
  61. }
  62. function _slicedToArray(arr, i) {
  63. return (
  64. _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest()
  65. );
  66. }
  67. function _nonIterableRest() {
  68. throw new TypeError('Invalid attempt to destructure non-iterable instance');
  69. }
  70. function _iterableToArrayLimit(arr, i) {
  71. var _arr = [];
  72. var _n = true;
  73. var _d = false;
  74. var _e = undefined;
  75. try {
  76. for (
  77. var _i = arr[Symbol.iterator](), _s;
  78. !(_n = (_s = _i.next()).done);
  79. _n = true
  80. ) {
  81. _arr.push(_s.value);
  82. if (i && _arr.length === i) break;
  83. }
  84. } catch (err) {
  85. _d = true;
  86. _e = err;
  87. } finally {
  88. try {
  89. if (!_n && _i['return'] != null) _i['return']();
  90. } finally {
  91. if (_d) throw _e;
  92. }
  93. }
  94. return _arr;
  95. }
  96. function _arrayWithHoles(arr) {
  97. if (Array.isArray(arr)) return arr;
  98. }
  99. function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  100. try {
  101. var info = gen[key](arg);
  102. var value = info.value;
  103. } catch (error) {
  104. reject(error);
  105. return;
  106. }
  107. if (info.done) {
  108. resolve(value);
  109. } else {
  110. Promise.resolve(value).then(_next, _throw);
  111. }
  112. }
  113. function _asyncToGenerator(fn) {
  114. return function() {
  115. var self = this,
  116. args = arguments;
  117. return new Promise(function(resolve, reject) {
  118. var gen = fn.apply(self, args);
  119. function _next(value) {
  120. asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
  121. }
  122. function _throw(err) {
  123. asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
  124. }
  125. _next(undefined);
  126. });
  127. };
  128. }
  129. function _defineProperty(obj, key, value) {
  130. if (key in obj) {
  131. Object.defineProperty(obj, key, {
  132. value: value,
  133. enumerable: true,
  134. configurable: true,
  135. writable: true
  136. });
  137. } else {
  138. obj[key] = value;
  139. }
  140. return obj;
  141. }
  142. // The default jest-runner is required because it is the default test runner
  143. // and required implicitly through the `runner` ProjectConfig option.
  144. _jestRunner().default;
  145. class TestScheduler {
  146. constructor(globalConfig, options, context) {
  147. _defineProperty(this, '_dispatcher', void 0);
  148. _defineProperty(this, '_globalConfig', void 0);
  149. _defineProperty(this, '_options', void 0);
  150. _defineProperty(this, '_context', void 0);
  151. this._dispatcher = new _ReporterDispatcher.default();
  152. this._globalConfig = globalConfig;
  153. this._options = options;
  154. this._context = context;
  155. this._setupReporters();
  156. }
  157. addReporter(reporter) {
  158. this._dispatcher.register(reporter);
  159. }
  160. removeReporter(ReporterClass) {
  161. this._dispatcher.unregister(ReporterClass);
  162. }
  163. scheduleTests(tests, watcher) {
  164. var _this = this;
  165. return _asyncToGenerator(function*() {
  166. const onStart = _this._dispatcher.onTestStart.bind(_this._dispatcher);
  167. const timings = [];
  168. const contexts = new Set();
  169. tests.forEach(test => {
  170. contexts.add(test.context);
  171. if (test.duration) {
  172. timings.push(test.duration);
  173. }
  174. });
  175. const aggregatedResults = createAggregatedResults(tests.length);
  176. const estimatedTime = Math.ceil(
  177. getEstimatedTime(timings, _this._globalConfig.maxWorkers) / 1000
  178. );
  179. const runInBand = (0, _testSchedulerHelper.shouldRunInBand)(
  180. tests,
  181. timings,
  182. _this._globalConfig
  183. );
  184. const onResult =
  185. /*#__PURE__*/
  186. (function() {
  187. var _ref = _asyncToGenerator(function*(test, testResult) {
  188. if (watcher.isInterrupted()) {
  189. return Promise.resolve();
  190. }
  191. if (testResult.testResults.length === 0) {
  192. const message = 'Your test suite must contain at least one test.';
  193. return onFailure(test, {
  194. message,
  195. stack: new Error(message).stack
  196. });
  197. } // Throws when the context is leaked after executing a test.
  198. if (testResult.leaks) {
  199. const message =
  200. _chalk().default.red.bold('EXPERIMENTAL FEATURE!\n') +
  201. 'Your test suite is leaking memory. Please ensure all references are cleaned.\n' +
  202. '\n' +
  203. 'There is a number of things that can leak memory:\n' +
  204. ' - Async operations that have not finished (e.g. fs.readFile).\n' +
  205. ' - Timers not properly mocked (e.g. setInterval, setTimeout).\n' +
  206. ' - Keeping references to the global scope.';
  207. return onFailure(test, {
  208. message,
  209. stack: new Error(message).stack
  210. });
  211. }
  212. (0, _testResult().addResult)(aggregatedResults, testResult);
  213. yield _this._dispatcher.onTestResult(
  214. test,
  215. testResult,
  216. aggregatedResults
  217. );
  218. return _this._bailIfNeeded(contexts, aggregatedResults, watcher);
  219. });
  220. return function onResult(_x, _x2) {
  221. return _ref.apply(this, arguments);
  222. };
  223. })();
  224. const onFailure =
  225. /*#__PURE__*/
  226. (function() {
  227. var _ref2 = _asyncToGenerator(function*(test, error) {
  228. if (watcher.isInterrupted()) {
  229. return;
  230. }
  231. const testResult = (0, _testResult().buildFailureTestResult)(
  232. test.path,
  233. error
  234. );
  235. testResult.failureMessage = (0, _jestMessageUtil().formatExecError)(
  236. testResult.testExecError,
  237. test.context.config,
  238. _this._globalConfig,
  239. test.path
  240. );
  241. (0, _testResult().addResult)(aggregatedResults, testResult);
  242. yield _this._dispatcher.onTestResult(
  243. test,
  244. testResult,
  245. aggregatedResults
  246. );
  247. });
  248. return function onFailure(_x3, _x4) {
  249. return _ref2.apply(this, arguments);
  250. };
  251. })();
  252. const updateSnapshotState = () => {
  253. contexts.forEach(context => {
  254. const status = _jestSnapshot().default.cleanup(
  255. context.hasteFS,
  256. _this._globalConfig.updateSnapshot,
  257. _jestSnapshot().default.buildSnapshotResolver(context.config),
  258. context.config.testPathIgnorePatterns
  259. );
  260. aggregatedResults.snapshot.filesRemoved += status.filesRemoved;
  261. aggregatedResults.snapshot.filesRemovedList = (
  262. aggregatedResults.snapshot.filesRemovedList || []
  263. ).concat(status.filesRemovedList);
  264. });
  265. const updateAll = _this._globalConfig.updateSnapshot === 'all';
  266. aggregatedResults.snapshot.didUpdate = updateAll;
  267. aggregatedResults.snapshot.failure = !!(
  268. !updateAll &&
  269. (aggregatedResults.snapshot.unchecked ||
  270. aggregatedResults.snapshot.unmatched ||
  271. aggregatedResults.snapshot.filesRemoved)
  272. );
  273. };
  274. yield _this._dispatcher.onRunStart(aggregatedResults, {
  275. estimatedTime,
  276. showStatus: !runInBand
  277. });
  278. const testRunners = Object.create(null);
  279. contexts.forEach(({config}) => {
  280. if (!testRunners[config.runner]) {
  281. const Runner = require(config.runner);
  282. testRunners[config.runner] = new Runner(_this._globalConfig, {
  283. changedFiles: _this._context && _this._context.changedFiles
  284. });
  285. }
  286. });
  287. const testsByRunner = _this._partitionTests(testRunners, tests);
  288. if (testsByRunner) {
  289. try {
  290. var _arr = Object.keys(testRunners);
  291. for (var _i = 0; _i < _arr.length; _i++) {
  292. const runner = _arr[_i];
  293. yield testRunners[runner].runTests(
  294. testsByRunner[runner],
  295. watcher,
  296. onStart,
  297. onResult,
  298. onFailure,
  299. {
  300. serial: runInBand || Boolean(testRunners[runner].isSerial)
  301. }
  302. );
  303. }
  304. } catch (error) {
  305. if (!watcher.isInterrupted()) {
  306. throw error;
  307. }
  308. }
  309. }
  310. updateSnapshotState();
  311. aggregatedResults.wasInterrupted = watcher.isInterrupted();
  312. yield _this._dispatcher.onRunComplete(contexts, aggregatedResults);
  313. const anyTestFailures = !(
  314. aggregatedResults.numFailedTests === 0 &&
  315. aggregatedResults.numRuntimeErrorTestSuites === 0
  316. );
  317. const anyReporterErrors = _this._dispatcher.hasErrors();
  318. aggregatedResults.success = !(
  319. anyTestFailures ||
  320. aggregatedResults.snapshot.failure ||
  321. anyReporterErrors
  322. );
  323. return aggregatedResults;
  324. })();
  325. }
  326. _partitionTests(testRunners, tests) {
  327. if (Object.keys(testRunners).length > 1) {
  328. return tests.reduce((testRuns, test) => {
  329. const runner = test.context.config.runner;
  330. if (!testRuns[runner]) {
  331. testRuns[runner] = [];
  332. }
  333. testRuns[runner].push(test);
  334. return testRuns;
  335. }, Object.create(null));
  336. } else if (tests.length > 0 && tests[0] != null) {
  337. // If there is only one runner, don't partition the tests.
  338. return Object.assign(Object.create(null), {
  339. [tests[0].context.config.runner]: tests
  340. });
  341. } else {
  342. return null;
  343. }
  344. }
  345. _shouldAddDefaultReporters(reporters) {
  346. return (
  347. !reporters ||
  348. !!reporters.find(
  349. reporter => this._getReporterProps(reporter).path === 'default'
  350. )
  351. );
  352. }
  353. _setupReporters() {
  354. const _this$_globalConfig = this._globalConfig,
  355. collectCoverage = _this$_globalConfig.collectCoverage,
  356. notify = _this$_globalConfig.notify,
  357. reporters = _this$_globalConfig.reporters;
  358. const isDefault = this._shouldAddDefaultReporters(reporters);
  359. if (isDefault) {
  360. this._setupDefaultReporters(collectCoverage);
  361. }
  362. if (!isDefault && collectCoverage) {
  363. this.addReporter(
  364. new (_reporters()).CoverageReporter(this._globalConfig, {
  365. changedFiles: this._context && this._context.changedFiles
  366. })
  367. );
  368. }
  369. if (notify) {
  370. this.addReporter(
  371. new (_reporters()).NotifyReporter(
  372. this._globalConfig,
  373. this._options.startRun,
  374. this._context
  375. )
  376. );
  377. }
  378. if (reporters && Array.isArray(reporters)) {
  379. this._addCustomReporters(reporters);
  380. }
  381. }
  382. _setupDefaultReporters(collectCoverage) {
  383. this.addReporter(
  384. this._globalConfig.verbose
  385. ? new (_reporters()).VerboseReporter(this._globalConfig)
  386. : new (_reporters()).DefaultReporter(this._globalConfig)
  387. );
  388. if (collectCoverage) {
  389. this.addReporter(
  390. new (_reporters()).CoverageReporter(this._globalConfig, {
  391. changedFiles: this._context && this._context.changedFiles
  392. })
  393. );
  394. }
  395. this.addReporter(new (_reporters()).SummaryReporter(this._globalConfig));
  396. }
  397. _addCustomReporters(reporters) {
  398. reporters.forEach(reporter => {
  399. const _this$_getReporterPro = this._getReporterProps(reporter),
  400. options = _this$_getReporterPro.options,
  401. path = _this$_getReporterPro.path;
  402. if (path === 'default') return;
  403. try {
  404. const Reporter = require(path);
  405. this.addReporter(new Reporter(this._globalConfig, options));
  406. } catch (error) {
  407. throw new Error(
  408. 'An error occurred while adding the reporter at path "' +
  409. path +
  410. '".' +
  411. error.message
  412. );
  413. }
  414. });
  415. }
  416. /**
  417. * Get properties of a reporter in an object
  418. * to make dealing with them less painful.
  419. */
  420. _getReporterProps(reporter) {
  421. if (typeof reporter === 'string') {
  422. return {
  423. options: this._options,
  424. path: reporter
  425. };
  426. } else if (Array.isArray(reporter)) {
  427. const _reporter = _slicedToArray(reporter, 2),
  428. path = _reporter[0],
  429. options = _reporter[1];
  430. return {
  431. options,
  432. path
  433. };
  434. }
  435. throw new Error('Reporter should be either a string or an array');
  436. }
  437. _bailIfNeeded(contexts, aggregatedResults, watcher) {
  438. if (
  439. this._globalConfig.bail !== 0 &&
  440. aggregatedResults.numFailedTests >= this._globalConfig.bail
  441. ) {
  442. if (watcher.isWatchMode()) {
  443. watcher.setState({
  444. interrupted: true
  445. });
  446. } else {
  447. const failureExit = () => (0, _exit().default)(1);
  448. return this._dispatcher
  449. .onRunComplete(contexts, aggregatedResults)
  450. .then(failureExit)
  451. .catch(failureExit);
  452. }
  453. }
  454. return Promise.resolve();
  455. }
  456. }
  457. exports.default = TestScheduler;
  458. const createAggregatedResults = numTotalTestSuites => {
  459. const result = (0, _testResult().makeEmptyAggregatedTestResult)();
  460. result.numTotalTestSuites = numTotalTestSuites;
  461. result.startTime = Date.now();
  462. result.success = false;
  463. return result;
  464. };
  465. const getEstimatedTime = (timings, workers) => {
  466. if (!timings.length) {
  467. return 0;
  468. }
  469. const max = Math.max.apply(null, timings);
  470. return timings.length <= workers
  471. ? max
  472. : Math.max(timings.reduce((sum, time) => sum + time) / workers, max);
  473. };