'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports.default = void 0;

var _types = require('./types');

var _state = require('./state');

var _utils = require('./utils');

var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
var Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}

function _asyncToGenerator(fn) {
  return function() {
    var self = this,
      args = arguments;
    return new Promise(function(resolve, reject) {
      var gen = fn.apply(self, args);
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
      }
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
      }
      _next(undefined);
    });
  };
}

const run =
  /*#__PURE__*/
  (function() {
    var _ref = _asyncToGenerator(function*() {
      const _getState = (0, _state.getState)(),
        rootDescribeBlock = _getState.rootDescribeBlock;

      (0, _state.dispatch)({
        name: 'run_start'
      });
      yield _runTestsForDescribeBlock(rootDescribeBlock);
      (0, _state.dispatch)({
        name: 'run_finish'
      });
      return (0,
      _utils.makeRunResult)((0, _state.getState)().rootDescribeBlock, (0, _state.getState)().unhandledErrors);
    });

    return function run() {
      return _ref.apply(this, arguments);
    };
  })();

const _runTestsForDescribeBlock =
  /*#__PURE__*/
  (function() {
    var _ref2 = _asyncToGenerator(function*(describeBlock) {
      (0, _state.dispatch)({
        describeBlock,
        name: 'run_describe_start'
      });

      const _getAllHooksForDescri = (0, _utils.getAllHooksForDescribe)(
          describeBlock
        ),
        beforeAll = _getAllHooksForDescri.beforeAll,
        afterAll = _getAllHooksForDescri.afterAll;

      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (
          var _iterator = beforeAll[Symbol.iterator](), _step;
          !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
          _iteratorNormalCompletion = true
        ) {
          const hook = _step.value;
          yield _callCircusHook({
            describeBlock,
            hook
          });
        } // Tests that fail and are retried we run after other tests
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return != null) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }

      const retryTimes = parseInt(global[_types.RETRY_TIMES], 10) || 0;
      const deferredRetryTests = [];
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (
          var _iterator2 = describeBlock.tests[Symbol.iterator](), _step2;
          !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done);
          _iteratorNormalCompletion2 = true
        ) {
          const test = _step2.value;
          const hasErrorsBeforeTestRun = test.errors.length > 0;
          yield _runTest(test);

          if (
            hasErrorsBeforeTestRun === false &&
            retryTimes > 0 &&
            test.errors.length > 0
          ) {
            deferredRetryTests.push(test);
          }
        } // Re-run failed tests n-times if configured
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
            _iterator2.return();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }

      for (var _i = 0; _i < deferredRetryTests.length; _i++) {
        const test = deferredRetryTests[_i];
        let numRetriesAvailable = retryTimes;

        while (numRetriesAvailable > 0 && test.errors.length > 0) {
          // Clear errors so retries occur
          (0, _state.dispatch)({
            name: 'test_retry',
            test
          });
          yield _runTest(test);
          numRetriesAvailable--;
        }
      }

      var _iteratorNormalCompletion3 = true;
      var _didIteratorError3 = false;
      var _iteratorError3 = undefined;

      try {
        for (
          var _iterator3 = describeBlock.children[Symbol.iterator](), _step3;
          !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done);
          _iteratorNormalCompletion3 = true
        ) {
          const child = _step3.value;
          yield _runTestsForDescribeBlock(child);
        }
      } catch (err) {
        _didIteratorError3 = true;
        _iteratorError3 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
            _iterator3.return();
          }
        } finally {
          if (_didIteratorError3) {
            throw _iteratorError3;
          }
        }
      }

      var _iteratorNormalCompletion4 = true;
      var _didIteratorError4 = false;
      var _iteratorError4 = undefined;

      try {
        for (
          var _iterator4 = afterAll[Symbol.iterator](), _step4;
          !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done);
          _iteratorNormalCompletion4 = true
        ) {
          const hook = _step4.value;
          yield _callCircusHook({
            describeBlock,
            hook
          });
        }
      } catch (err) {
        _didIteratorError4 = true;
        _iteratorError4 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
            _iterator4.return();
          }
        } finally {
          if (_didIteratorError4) {
            throw _iteratorError4;
          }
        }
      }

      (0, _state.dispatch)({
        describeBlock,
        name: 'run_describe_finish'
      });
    });

    return function _runTestsForDescribeBlock(_x) {
      return _ref2.apply(this, arguments);
    };
  })();

const _runTest =
  /*#__PURE__*/
  (function() {
    var _ref3 = _asyncToGenerator(function*(test) {
      (0, _state.dispatch)({
        name: 'test_start',
        test
      });
      const testContext = Object.create(null);

      const _getState2 = (0, _state.getState)(),
        hasFocusedTests = _getState2.hasFocusedTests,
        testNamePattern = _getState2.testNamePattern;

      const isSkipped =
        test.mode === 'skip' ||
        (hasFocusedTests && test.mode !== 'only') ||
        (testNamePattern && !testNamePattern.test((0, _utils.getTestID)(test)));

      if (isSkipped) {
        (0, _state.dispatch)({
          name: 'test_skip',
          test
        });
        return;
      }

      if (test.mode === 'todo') {
        (0, _state.dispatch)({
          name: 'test_todo',
          test
        });
        return;
      }

      const _getEachHooksForTest = (0, _utils.getEachHooksForTest)(test),
        afterEach = _getEachHooksForTest.afterEach,
        beforeEach = _getEachHooksForTest.beforeEach;

      var _iteratorNormalCompletion5 = true;
      var _didIteratorError5 = false;
      var _iteratorError5 = undefined;

      try {
        for (
          var _iterator5 = beforeEach[Symbol.iterator](), _step5;
          !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done);
          _iteratorNormalCompletion5 = true
        ) {
          const hook = _step5.value;

          if (test.errors.length) {
            // If any of the before hooks failed already, we don't run any
            // hooks after that.
            break;
          }

          yield _callCircusHook({
            hook,
            test,
            testContext
          });
        }
      } catch (err) {
        _didIteratorError5 = true;
        _iteratorError5 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
            _iterator5.return();
          }
        } finally {
          if (_didIteratorError5) {
            throw _iteratorError5;
          }
        }
      }

      yield _callCircusTest(test, testContext);
      var _iteratorNormalCompletion6 = true;
      var _didIteratorError6 = false;
      var _iteratorError6 = undefined;

      try {
        for (
          var _iterator6 = afterEach[Symbol.iterator](), _step6;
          !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done);
          _iteratorNormalCompletion6 = true
        ) {
          const hook = _step6.value;
          yield _callCircusHook({
            hook,
            test,
            testContext
          });
        } // `afterAll` hooks should not affect test status (pass or fail), because if
        // we had a global `afterAll` hook it would block all existing tests until
        // this hook is executed. So we dispatch `test_done` right away.
      } catch (err) {
        _didIteratorError6 = true;
        _iteratorError6 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion6 && _iterator6.return != null) {
            _iterator6.return();
          }
        } finally {
          if (_didIteratorError6) {
            throw _iteratorError6;
          }
        }
      }

      (0, _state.dispatch)({
        name: 'test_done',
        test
      });
    });

    return function _runTest(_x2) {
      return _ref3.apply(this, arguments);
    };
  })();

const _callCircusHook = ({hook, test, describeBlock, testContext}) => {
  (0, _state.dispatch)({
    hook,
    name: 'hook_start'
  });
  const timeout = hook.timeout || (0, _state.getState)().testTimeout;
  return (0, _utils.callAsyncCircusFn)(hook.fn, testContext, {
    isHook: true,
    timeout
  })
    .then(() =>
      (0, _state.dispatch)({
        describeBlock,
        hook,
        name: 'hook_success',
        test
      })
    )
    .catch(error =>
      (0, _state.dispatch)({
        describeBlock,
        error,
        hook,
        name: 'hook_failure',
        test
      })
    );
};

const _callCircusTest = (test, testContext) => {
  (0, _state.dispatch)({
    name: 'test_fn_start',
    test
  });
  const timeout = test.timeout || (0, _state.getState)().testTimeout;
  (0, _utils.invariant)(
    test.fn,
    `Tests with no 'fn' should have 'mode' set to 'skipped'`
  );

  if (test.errors.length) {
    // We don't run the test if there's already an error in before hooks.
    return Promise.resolve();
  }

  return (0, _utils.callAsyncCircusFn)(test.fn, testContext, {
    isHook: false,
    timeout
  })
    .then(() =>
      (0, _state.dispatch)({
        name: 'test_fn_success',
        test
      })
    )
    .catch(error =>
      (0, _state.dispatch)({
        error,
        name: 'test_fn_failure',
        test
      })
    );
};

var _default = run;
exports.default = _default;