index.js 6.0 KB

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