123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- 'use strict';
- Object.defineProperty(exports, '__esModule', {
- value: true
- });
- exports.default = void 0;
- function _chalk() {
- const data = _interopRequireDefault(require('chalk'));
- _chalk = function () {
- return data;
- };
- return data;
- }
- function _emittery() {
- const data = _interopRequireDefault(require('emittery'));
- _emittery = function () {
- return data;
- };
- return data;
- }
- function _throat() {
- const data = _interopRequireDefault(require('throat'));
- _throat = function () {
- return data;
- };
- return data;
- }
- function _jestUtil() {
- const data = require('jest-util');
- _jestUtil = function () {
- return data;
- };
- return data;
- }
- function _jestWorker() {
- const data = require('jest-worker');
- _jestWorker = function () {
- return data;
- };
- return data;
- }
- var _runTest = _interopRequireDefault(require('./runTest'));
- function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : {default: obj};
- }
- 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 TEST_WORKER_PATH = require.resolve('./testWorker');
- class TestRunner {
- constructor(globalConfig, context) {
- _defineProperty(this, '_globalConfig', void 0);
- _defineProperty(this, '_context', void 0);
- _defineProperty(this, 'eventEmitter', new (_emittery().default)());
- _defineProperty(
- this,
- '__PRIVATE_UNSTABLE_API_supportsEventEmitters__',
- true
- );
- _defineProperty(this, 'isSerial', void 0);
- this._globalConfig = globalConfig;
- this._context = context || {};
- }
- async runTests(tests, watcher, onStart, onResult, onFailure, options) {
- return await (options.serial
- ? this._createInBandTestRun(tests, watcher, onStart, onResult, onFailure)
- : this._createParallelTestRun(
- tests,
- watcher,
- onStart,
- onResult,
- onFailure
- ));
- }
- async _createInBandTestRun(tests, watcher, onStart, onResult, onFailure) {
- process.env.JEST_WORKER_ID = '1';
- const mutex = (0, _throat().default)(1);
- return tests.reduce(
- (promise, test) =>
- mutex(() =>
- promise
- .then(async () => {
- if (watcher.isInterrupted()) {
- throw new CancelRun();
- } // Remove `if(onStart)` in Jest 27
- if (onStart) {
- await onStart(test);
- return (0, _runTest.default)(
- test.path,
- this._globalConfig,
- test.context.config,
- test.context.resolver,
- this._context,
- undefined
- );
- } // `deepCyclicCopy` used here to avoid mem-leak
- const sendMessageToJest = (eventName, args) =>
- this.eventEmitter.emit(
- eventName,
- (0, _jestUtil().deepCyclicCopy)(args, {
- keepPrototype: false
- })
- );
- await this.eventEmitter.emit('test-file-start', [test]);
- return (0, _runTest.default)(
- test.path,
- this._globalConfig,
- test.context.config,
- test.context.resolver,
- this._context,
- sendMessageToJest
- );
- })
- .then(result => {
- if (onResult) {
- return onResult(test, result);
- }
- return this.eventEmitter.emit('test-file-success', [
- test,
- result
- ]);
- })
- .catch(err => {
- if (onFailure) {
- return onFailure(test, err);
- }
- return this.eventEmitter.emit('test-file-failure', [test, err]);
- })
- ),
- Promise.resolve()
- );
- }
- async _createParallelTestRun(tests, watcher, onStart, onResult, onFailure) {
- const resolvers = new Map();
- for (const test of tests) {
- if (!resolvers.has(test.context.config.name)) {
- resolvers.set(test.context.config.name, {
- config: test.context.config,
- serializableModuleMap: test.context.moduleMap.toJSON()
- });
- }
- }
- const worker = new (_jestWorker().Worker)(TEST_WORKER_PATH, {
- exposedMethods: ['worker'],
- forkOptions: {
- stdio: 'pipe'
- },
- maxRetries: 3,
- numWorkers: this._globalConfig.maxWorkers,
- setupArgs: [
- {
- serializableResolvers: Array.from(resolvers.values())
- }
- ]
- });
- if (worker.getStdout()) worker.getStdout().pipe(process.stdout);
- if (worker.getStderr()) worker.getStderr().pipe(process.stderr);
- const mutex = (0, _throat().default)(this._globalConfig.maxWorkers); // Send test suites to workers continuously instead of all at once to track
- // the start time of individual tests.
- const runTestInWorker = test =>
- mutex(async () => {
- if (watcher.isInterrupted()) {
- return Promise.reject();
- } // Remove `if(onStart)` in Jest 27
- if (onStart) {
- await onStart(test);
- } else {
- await this.eventEmitter.emit('test-file-start', [test]);
- }
- const promise = worker.worker({
- config: test.context.config,
- context: {
- ...this._context,
- changedFiles:
- this._context.changedFiles &&
- Array.from(this._context.changedFiles),
- sourcesRelatedToTestsInChangedFiles:
- this._context.sourcesRelatedToTestsInChangedFiles &&
- Array.from(this._context.sourcesRelatedToTestsInChangedFiles)
- },
- globalConfig: this._globalConfig,
- path: test.path
- });
- if (promise.UNSTABLE_onCustomMessage) {
- // TODO: Get appropriate type for `onCustomMessage`
- promise.UNSTABLE_onCustomMessage(([event, payload]) => {
- this.eventEmitter.emit(event, payload);
- });
- }
- return promise;
- });
- const onInterrupt = new Promise((_, reject) => {
- watcher.on('change', state => {
- if (state.interrupted) {
- reject(new CancelRun());
- }
- });
- });
- const runAllTests = Promise.all(
- tests.map(test =>
- runTestInWorker(test)
- .then(result => {
- if (onResult) {
- return onResult(test, result);
- }
- return this.eventEmitter.emit('test-file-success', [test, result]);
- })
- .catch(error => {
- if (onFailure) {
- return onFailure(test, error);
- }
- return this.eventEmitter.emit('test-file-failure', [test, error]);
- })
- )
- );
- const cleanup = async () => {
- const {forceExited} = await worker.end();
- if (forceExited) {
- console.error(
- _chalk().default.yellow(
- 'A worker process has failed to exit gracefully and has been force exited. ' +
- 'This is likely caused by tests leaking due to improper teardown. ' +
- 'Try running with --detectOpenHandles to find leaks. ' +
- 'Active timers can also cause this, ensure that .unref() was called on them.'
- )
- );
- }
- };
- return Promise.race([runAllTests, onInterrupt]).then(cleanup, cleanup);
- }
- on(eventName, listener) {
- return this.eventEmitter.on(eventName, listener);
- }
- }
- exports.default = TestRunner;
- class CancelRun extends Error {
- constructor(message) {
- super(message);
- this.name = 'CancelRun';
- }
- }
|