text: 'use strict';
  
  function _exit() {
    const data = _interopRequireDefault(require('exit'));
  
    _exit = function () {
      return data;
    };
  
    return data;
  }
  
  function _chalk() {
    const data = _interopRequireDefault(require('chalk'));
  
    _chalk = function () {
      return data;
    };
  
    return data;
  }
  
  function _throat() {
    const data = _interopRequireDefault(require('throat'));
  
    _throat = function () {
      return data;
    };
  
    return data;
  }
  
  function _jestWorker() {
    const data = _interopRequireDefault(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');
  
  /* eslint-disable-next-line no-redeclare */
  class TestRunner {
    constructor(globalConfig, context) {
      _defineProperty(this, '_globalConfig', void 0);
  
      _defineProperty(this, '_context', void 0);
  
      _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();
                }
  
                await onStart(test);
                return (0, _runTest.default)(
                  test.path,
                  this._globalConfig,
                  test.context.config,
                  test.context.resolver,
                  this._context
                );
              })
              .then(result => onResult(test, result))
              .catch(err => onFailure(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().default)(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();
          }
  
          await onStart(test);
          return worker.worker({
            config: test.context.config,
            context: {
              ...this._context,
              changedFiles:
                this._context.changedFiles &&
                Array.from(this._context.changedFiles)
            },
            globalConfig: this._globalConfig,
            path: test.path
          });
        });
  
      const onError = async (err, test) => {
        await onFailure(test, err);
  
        if (err.type === 'ProcessTerminatedError') {
          console.error(
            'A worker process has quit unexpectedly! ' +
              'Most likely this is an initialization error.'
          );
          (0, _exit().default)(1);
        }
      };
  
      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(testResult => onResult(test, testResult))
            .catch(error => onError(error, test))
        )
      );
  
      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 --runInBand --detectOpenHandles to find leaks.'
            )
          );
        }
      };
  
      return Promise.race([runAllTests, onInterrupt]).then(cleanup, cleanup);
    }
  }
  
  class CancelRun extends Error {
    constructor(message) {
      super(message);
      this.name = 'CancelRun';
    }
  }
  
  module.exports = TestRunner;
  