function next(i) {
      var hook = hooks[i];
      if (!hook) {
        return fn();
      }
      self.currentRunnable = hook;

      if (name === HOOK_TYPE_BEFORE_ALL) {
        hook.ctx.currentTest = hook.parent.tests[0];
      } else if (name === HOOK_TYPE_AFTER_ALL) {
        hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1];
      } else {
        hook.ctx.currentTest = self.test;
      }

      setHookTitle(hook);

      hook.allowUncaught = self.allowUncaught;

      self.emit(constants$1.EVENT_HOOK_BEGIN, hook);

      if (!hook.listeners('error').length) {
        self._addEventListener(hook, 'error', function (err) {
          self.fail(hook, err);
        });
      }

      hook.run(function cbHookRun(err) {
        var testError = hook.error();
        if (testError) {
          self.fail(self.test, testError);
        }
        // conditional skip
        if (hook.pending) {
          if (name === HOOK_TYPE_AFTER_EACH) {
            // TODO define and implement use case
            if (self.test) {
              self.test.pending = true;
            }
          } else if (name === HOOK_TYPE_BEFORE_EACH) {
            if (self.test) {
              self.test.pending = true;
            }
            self.emit(constants$1.EVENT_HOOK_END, hook);
            hook.pending = false; // activates hook for next test
            return fn(new Error('abort hookDown'));
          } else if (name === HOOK_TYPE_BEFORE_ALL) {
            suite.tests.forEach(function (test) {
              test.pending = true;
            });
            suite.suites.forEach(function (suite) {
              suite.pending = true;
            });
            hooks = [];
          } else {
            hook.pending = false;
            var errForbid = createUnsupportedError$1('`this.skip` forbidden');
            self.fail(hook, errForbid);
            return fn(errForbid);
          }
        } else if (err) {
          self.fail(hook, err);
          // stop executing hooks, notify callee of hook err
          return fn(err);
        }
        self.emit(constants$1.EVENT_HOOK_END, hook);
        delete hook.ctx.currentTest;
        setHookTitle(hook);
        next(++i);
      });

      function setHookTitle(hook) {
        hook.originalTitle = hook.originalTitle || hook.title;
        if (hook.ctx && hook.ctx.currentTest) {
          hook.title = `${hook.originalTitle} for "${hook.ctx.currentTest.title}"`;
        } else {
          var parentTitle;
          if (hook.parent.title) {
            parentTitle = hook.parent.title;
          } else {
            parentTitle = hook.parent.root ? '{root}' : '';
          }
          hook.title = `${hook.originalTitle} in "${parentTitle}"`;
        }
      }
    }
Run hook `name` callbacks and then invoke `fn()`.

@private
@param {string} name
@param {Function} fn