index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. function _fs() {
  7. const data = _interopRequireDefault(require('fs'));
  8. _fs = function _fs() {
  9. return data;
  10. };
  11. return data;
  12. }
  13. function _jestHasteMap() {
  14. const data = require('jest-haste-map');
  15. _jestHasteMap = function _jestHasteMap() {
  16. return data;
  17. };
  18. return data;
  19. }
  20. function _interopRequireDefault(obj) {
  21. return obj && obj.__esModule ? obj : {default: obj};
  22. }
  23. function _defineProperty(obj, key, value) {
  24. if (key in obj) {
  25. Object.defineProperty(obj, key, {
  26. value: value,
  27. enumerable: true,
  28. configurable: true,
  29. writable: true
  30. });
  31. } else {
  32. obj[key] = value;
  33. }
  34. return obj;
  35. }
  36. const FAIL = 0;
  37. const SUCCESS = 1;
  38. /**
  39. * The TestSequencer will ultimately decide which tests should run first.
  40. * It is responsible for storing and reading from a local cache
  41. * map that stores context information for a given test, such as how long it
  42. * took to run during the last run and if it has failed or not.
  43. * Such information is used on:
  44. * TestSequencer.sort(tests: Array<Test>)
  45. * to sort the order of the provided tests.
  46. *
  47. * After the results are collected,
  48. * TestSequencer.cacheResults(tests: Array<Test>, results: AggregatedResult)
  49. * is called to store/update this information on the cache map.
  50. */
  51. class TestSequencer {
  52. constructor() {
  53. _defineProperty(this, '_cache', new Map());
  54. }
  55. _getCachePath(context) {
  56. const config = context.config;
  57. return (0, _jestHasteMap().getCacheFilePath)(
  58. config.cacheDirectory,
  59. 'perf-cache-' + config.name
  60. );
  61. }
  62. _getCache(test) {
  63. const context = test.context;
  64. if (!this._cache.has(context) && context.config.cache) {
  65. const cachePath = this._getCachePath(context);
  66. if (_fs().default.existsSync(cachePath)) {
  67. try {
  68. this._cache.set(
  69. context,
  70. JSON.parse(_fs().default.readFileSync(cachePath, 'utf8'))
  71. );
  72. } catch (e) {}
  73. }
  74. }
  75. let cache = this._cache.get(context);
  76. if (!cache) {
  77. cache = {};
  78. this._cache.set(context, cache);
  79. }
  80. return cache;
  81. }
  82. /**
  83. * Sorting tests is very important because it has a great impact on the
  84. * user-perceived responsiveness and speed of the test run.
  85. *
  86. * If such information is on cache, tests are sorted based on:
  87. * -> Has it failed during the last run ?
  88. * Since it's important to provide the most expected feedback as quickly
  89. * as possible.
  90. * -> How long it took to run ?
  91. * Because running long tests first is an effort to minimize worker idle
  92. * time at the end of a long test run.
  93. * And if that information is not available they are sorted based on file size
  94. * since big test files usually take longer to complete.
  95. *
  96. * Note that a possible improvement would be to analyse other information
  97. * from the file other than its size.
  98. *
  99. */
  100. sort(tests) {
  101. const stats = {};
  102. const fileSize = ({path, context: {hasteFS}}) =>
  103. stats[path] || (stats[path] = hasteFS.getSize(path) || 0);
  104. const hasFailed = (cache, test) =>
  105. cache[test.path] && cache[test.path][0] === FAIL;
  106. const time = (cache, test) => cache[test.path] && cache[test.path][1];
  107. tests.forEach(test => (test.duration = time(this._getCache(test), test)));
  108. return tests.sort((testA, testB) => {
  109. const cacheA = this._getCache(testA);
  110. const cacheB = this._getCache(testB);
  111. const failedA = hasFailed(cacheA, testA);
  112. const failedB = hasFailed(cacheB, testB);
  113. const hasTimeA = testA.duration != null;
  114. if (failedA !== failedB) {
  115. return failedA ? -1 : 1;
  116. } else if (hasTimeA != (testB.duration != null)) {
  117. // If only one of two tests has timing information, run it last
  118. return hasTimeA ? 1 : -1;
  119. } else if (testA.duration != null && testB.duration != null) {
  120. return testA.duration < testB.duration ? 1 : -1;
  121. } else {
  122. return fileSize(testA) < fileSize(testB) ? 1 : -1;
  123. }
  124. });
  125. }
  126. cacheResults(tests, results) {
  127. const map = Object.create(null);
  128. tests.forEach(test => (map[test.path] = test));
  129. results.testResults.forEach(testResult => {
  130. if (testResult && map[testResult.testFilePath] && !testResult.skipped) {
  131. const cache = this._getCache(map[testResult.testFilePath]);
  132. const perf = testResult.perfStats;
  133. cache[testResult.testFilePath] = [
  134. testResult.numFailingTests ? FAIL : SUCCESS,
  135. perf.end - perf.start || 0
  136. ];
  137. }
  138. });
  139. this._cache.forEach((cache, context) =>
  140. _fs().default.writeFileSync(
  141. this._getCachePath(context),
  142. JSON.stringify(cache)
  143. )
  144. );
  145. }
  146. }
  147. exports.default = TestSequencer;