text: const {Logger} = require('../utils');
  
  module.exports = class {
  
    constructor(proc = process) {
      this.__exitCode = 0;
      this.testRunner = null;
      this.process = proc;
      this.finishCallback = null;
  
      this.addExitListener();
      this.process.once('uncaughtException', err => {
        this.uncaught(err);
      });
  
      this.process.on('unhandledRejection', this.unhandled.bind(this));
    }
  
    addExitListener() {
      // when used programmatically, nightwatch will keep adding exit listeners for each test suite
      const listeners = this.process.listeners('exit');
      const isAlreadyAdded = listeners.find(item => {
        return item.name.includes('exitHandlerNightwatch');
      });
  
      if (!isAlreadyAdded) {
        this.exitHandler = function exitHandlerNightwatch() {
          return this.onExit();
        }.bind(this);
        this.process.on('exit', this.exitHandler);
      }
    }
  
    setTestRunner(testRunner) {
      this.testRunner = testRunner;
  
      return this;
    }
  
    setExitCode(code) {
      this.__exitCode = code;
  
      return this;
    }
  
    get exitCode() {
      return this.__exitCode;
    }
  
    onExit(code) {
      if (code > 0) {
        this.setExitCode(code);
      }
  
      this.exit();
    }
  
    unhandled(err) {
      this.uncaught(err, {type: 'unhandledRejection'});
    }
  
    getCurrentPromise(err) {
      if (this.testRunner) {
        const {currentSuite} = this.testRunner;
  
        if (currentSuite && (currentSuite.uncaughtError instanceof Error)) {
          Logger.error('An additional uncaught error occurred while trying to handle the previous one – ' + err.stack);
  
          return;
        }
  
        this.testRunner.registerUncaughtErr(err);
        if (currentSuite) {
          currentSuite.emptyQueue();
          currentSuite.setUncaughtError(err);
        }
  
        if (this.testRunner.publishReport) {
          this.testRunner.publishReport = false;
          this.testRunner.reportResults().then(() => {}).catch(err => console.error(err));
        }
      }
    }
  
    uncaught(err, {type = 'uncaughtException'} = {}) {
      Logger.warn(`${type}: ${err.message}\n${err.stack}`);
  
      if (['TimeoutError', 'NoSuchElementError'].includes(err.name) && this.testRunner.type !== 'cucumber') {
        this.closeProcess(err);
        if (this.testRunner && this.testRunner.publishReport) {
          this.testRunner.publishReport = false;
          this.testRunner.reportResults().catch(() => {}).then(function() {});
        }
  
        return;
      }
  
      this.getCurrentPromise(err);
  
      return this.closeProcess(err);
    }
  
    closeProcess(err) {
      if (this.finishCallback) {
        this.finishCallback(err);
      }
  
      this.setExitCode(1).exit();
    }
  
    exit() {
      this.process.exit && this.process.exit(this.exitCode);
  
      return this;
    }
  };
  