Spaces:
Sleeping
Sleeping
| ; | |
| Object.defineProperty(exports, '__esModule', { | |
| value: true | |
| }); | |
| exports.default = void 0; | |
| function fs() { | |
| const data = _interopRequireWildcard(require('graceful-fs')); | |
| fs = function () { | |
| return data; | |
| }; | |
| return data; | |
| } | |
| function _jestHasteMap() { | |
| const data = _interopRequireDefault(require('jest-haste-map')); | |
| _jestHasteMap = function () { | |
| return data; | |
| }; | |
| return data; | |
| } | |
| function _interopRequireDefault(obj) { | |
| return obj && obj.__esModule ? obj : {default: obj}; | |
| } | |
| function _getRequireWildcardCache(nodeInterop) { | |
| if (typeof WeakMap !== 'function') return null; | |
| var cacheBabelInterop = new WeakMap(); | |
| var cacheNodeInterop = new WeakMap(); | |
| return (_getRequireWildcardCache = function (nodeInterop) { | |
| return nodeInterop ? cacheNodeInterop : cacheBabelInterop; | |
| })(nodeInterop); | |
| } | |
| function _interopRequireWildcard(obj, nodeInterop) { | |
| if (!nodeInterop && obj && obj.__esModule) { | |
| return obj; | |
| } | |
| if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { | |
| return {default: obj}; | |
| } | |
| var cache = _getRequireWildcardCache(nodeInterop); | |
| if (cache && cache.has(obj)) { | |
| return cache.get(obj); | |
| } | |
| var newObj = {}; | |
| var hasPropertyDescriptor = | |
| Object.defineProperty && Object.getOwnPropertyDescriptor; | |
| for (var key in obj) { | |
| if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) { | |
| var desc = hasPropertyDescriptor | |
| ? Object.getOwnPropertyDescriptor(obj, key) | |
| : null; | |
| if (desc && (desc.get || desc.set)) { | |
| Object.defineProperty(newObj, key, desc); | |
| } else { | |
| newObj[key] = obj[key]; | |
| } | |
| } | |
| } | |
| newObj.default = obj; | |
| if (cache) { | |
| cache.set(obj, newObj); | |
| } | |
| return newObj; | |
| } | |
| function _defineProperty(obj, key, value) { | |
| if (key in obj) { | |
| Object.defineProperty(obj, key, { | |
| value: value, | |
| enumerable: true, | |
| configurable: true, | |
| writable: true | |
| }); | |
| } else { | |
| obj[key] = value; | |
| } | |
| return obj; | |
| } | |
| const FAIL = 0; | |
| const SUCCESS = 1; | |
| /** | |
| * The TestSequencer will ultimately decide which tests should run first. | |
| * It is responsible for storing and reading from a local cache | |
| * map that stores context information for a given test, such as how long it | |
| * took to run during the last run and if it has failed or not. | |
| * Such information is used on: | |
| * TestSequencer.sort(tests: Array<Test>) | |
| * to sort the order of the provided tests. | |
| * | |
| * After the results are collected, | |
| * TestSequencer.cacheResults(tests: Array<Test>, results: AggregatedResult) | |
| * is called to store/update this information on the cache map. | |
| */ | |
| class TestSequencer { | |
| constructor() { | |
| _defineProperty(this, '_cache', new Map()); | |
| } | |
| _getCachePath(context) { | |
| const {config} = context; | |
| const HasteMapClass = _jestHasteMap().default.getStatic(config); | |
| return HasteMapClass.getCacheFilePath( | |
| config.cacheDirectory, | |
| 'perf-cache-' + config.name | |
| ); | |
| } | |
| _getCache(test) { | |
| const {context} = test; | |
| if (!this._cache.has(context) && context.config.cache) { | |
| const cachePath = this._getCachePath(context); | |
| if (fs().existsSync(cachePath)) { | |
| try { | |
| this._cache.set( | |
| context, | |
| JSON.parse(fs().readFileSync(cachePath, 'utf8')) | |
| ); | |
| } catch {} | |
| } | |
| } | |
| let cache = this._cache.get(context); | |
| if (!cache) { | |
| cache = {}; | |
| this._cache.set(context, cache); | |
| } | |
| return cache; | |
| } | |
| /** | |
| * Sorting tests is very important because it has a great impact on the | |
| * user-perceived responsiveness and speed of the test run. | |
| * | |
| * If such information is on cache, tests are sorted based on: | |
| * -> Has it failed during the last run ? | |
| * Since it's important to provide the most expected feedback as quickly | |
| * as possible. | |
| * -> How long it took to run ? | |
| * Because running long tests first is an effort to minimize worker idle | |
| * time at the end of a long test run. | |
| * And if that information is not available they are sorted based on file size | |
| * since big test files usually take longer to complete. | |
| * | |
| * Note that a possible improvement would be to analyse other information | |
| * from the file other than its size. | |
| * | |
| */ | |
| sort(tests) { | |
| const stats = {}; | |
| const fileSize = ({path, context: {hasteFS}}) => | |
| stats[path] || (stats[path] = hasteFS.getSize(path) || 0); | |
| const hasFailed = (cache, test) => | |
| cache[test.path] && cache[test.path][0] === FAIL; | |
| const time = (cache, test) => cache[test.path] && cache[test.path][1]; | |
| tests.forEach(test => (test.duration = time(this._getCache(test), test))); | |
| return tests.sort((testA, testB) => { | |
| const cacheA = this._getCache(testA); | |
| const cacheB = this._getCache(testB); | |
| const failedA = hasFailed(cacheA, testA); | |
| const failedB = hasFailed(cacheB, testB); | |
| const hasTimeA = testA.duration != null; | |
| if (failedA !== failedB) { | |
| return failedA ? -1 : 1; | |
| } else if (hasTimeA != (testB.duration != null)) { | |
| // If only one of two tests has timing information, run it last | |
| return hasTimeA ? 1 : -1; | |
| } else if (testA.duration != null && testB.duration != null) { | |
| return testA.duration < testB.duration ? 1 : -1; | |
| } else { | |
| return fileSize(testA) < fileSize(testB) ? 1 : -1; | |
| } | |
| }); | |
| } | |
| allFailedTests(tests) { | |
| const hasFailed = (cache, test) => { | |
| var _cache$test$path; | |
| return ( | |
| ((_cache$test$path = cache[test.path]) === null || | |
| _cache$test$path === void 0 | |
| ? void 0 | |
| : _cache$test$path[0]) === FAIL | |
| ); | |
| }; | |
| return this.sort( | |
| tests.filter(test => hasFailed(this._getCache(test), test)) | |
| ); | |
| } | |
| cacheResults(tests, results) { | |
| const map = Object.create(null); | |
| tests.forEach(test => (map[test.path] = test)); | |
| results.testResults.forEach(testResult => { | |
| if (testResult && map[testResult.testFilePath] && !testResult.skipped) { | |
| const cache = this._getCache(map[testResult.testFilePath]); | |
| const perf = testResult.perfStats; | |
| cache[testResult.testFilePath] = [ | |
| testResult.numFailingTests ? FAIL : SUCCESS, | |
| perf.runtime || 0 | |
| ]; | |
| } | |
| }); | |
| this._cache.forEach((cache, context) => | |
| fs().writeFileSync(this._getCachePath(context), JSON.stringify(cache)) | |
| ); | |
| } | |
| } | |
| exports.default = TestSequencer; | |