index.js 6.3 KB

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