Source: Mock.js

'use strict';

var Expectation = require('./Expectation.js');
var UnexpectedFunctionCallError = require('./Error/UnexpectedFunctionCallError.js');

/**
 * Flag to have mocks not throw an error if they are called unexpectedly
 * @global
 * @private
 */
var _ignoreOtherCalls = false;

/**
 * Pointer to currently executing tree so that unexpected calls can display expected calls in error message.
 * @global
 * @private
 */
var _tree;

/**
 * Represents a mocked function.
 */
class Mock {
  /**
   * Creates a new mocked function.
   * @param {string} [name=<anonymous>] Name of the function.
   */
  constructor(name) {
    /**
     * Name of the mocked function.
     * @name Mock#name
     * @type {string}
     */
    this.name = name || '<anonymous>';

    let mock = function() {
      return mock._class.handler(Array.from(arguments));
    };

    /**
     *
     */
    this.function = mock;
    this.function._class = this;

    mock.shouldBeCalled = function() {
      return mock._class.shouldBeCalled();
    };

    mock.shouldBeCalledWith = function() {
      return mock._class.shouldBeCalledWith(...arguments);
    };

    mock.shouldBeCalledWithAnyArguments = function() {
      return mock._class.shouldBeCalledWithAnyArguments();
    };

    mock.mayBeCalled = function() {
      return mock._class.mayBeCalled();
    };

    mock.mayBeCalledWith = function() {
      return mock._class.mayBeCalledWith(...arguments);
    };

    mock.mayBeCalledWithAnyArguments = function() {
      return mock._class.mayBeCalledWithAnyArguments();
    };

    this.reset();

    // this class is exposed to the user via a function version of itself
    // internal code sees the class version
    return mock;
  }

  /**
   * Gets state of global ignore other calls flag
   */
  get ignoreOtherCalls() {
    return _ignoreOtherCalls;
  }

  /**
   * Sets state of the global ignore other calls flag
   */
  set ignoreOtherCalls(value) {
    _ignoreOtherCalls = value;
  }

  /**
   * Gets global tree pointer value.
   */
  get tree() {
    return _tree;
  }

  /**
   * Sets global tree pointer value
   */
  set tree(value) {
    _tree = value;
  }

  /*
   * Resets Mock globals and this mocks execution handler
   */
  reset() {
    this.ignoreOtherCalls = false;
    this.tree = undefined;

    /**
     * The function that gets executed when the mocked function is called
     * @name Mock#handler
     * @type function
     */
    this.handler = this._defaultHandler;
  }

  /*
   * Default execution handler
   * @param {object[]} args Arguments passed to mock during execution.
   */
  _defaultHandler(args) {
    if (!this.ignoreOtherCalls) {
      let calls = [];

      if (this.tree !== undefined) {
        calls = this.tree._calls;
      }

      throw new UnexpectedFunctionCallError(this, args, calls);
    }
  }

  /**
   * Creats a new required {@link Expecatation} from this mock.
   * @returns {Expectation} Expectation created from this mock.
   */
  shouldBeCalled() {
    return new Expectation(this, true);
  }

  /**
   * Creats a new required {@link Expecatation} from this mock that expects the specified arguments.
   * @param {object[]} arguments Expected arguments
   * @returns {Expectation} Expectation created from this mock.
   */
  shouldBeCalledWith() {
    return this.shouldBeCalled()
      .withTheseArguments(...arguments);
  }

  /**
   * Creats a new required {@link Expecatation} from this mock that will accept any arguments.
   * @returns {Expectation} Expectation created from this mock.
   */
  shouldBeCalledWithAnyArguments() {
    return this.shouldBeCalled()
      .withAnyArguments();
  }

  /**
   * Creats a new optional {@link Expecatation} from this mock.
   * @returns {Expectation} Expectation created from this mock.
   */
  mayBeCalled() {
    return new Expectation(this, false);
  }

  /**
   * Creats a new optional {@link Expecatation} from this mock that expects the specified arguments.
   * @param {object[]} arguments Expected arguments
   * @returns {Expectation} Expectation created from this mock.
   */
  mayBeCalledWith() {
    return this.mayBeCalled()
      .withTheseArguments(...arguments);
  }

  /**
   * Creats a new optional {@link Expecatation} from this mock that will accept any arguments.
   * @returns {Expectation} Expectation created from this mock.
   */
  mayBeCalledWithAnyArguments() {
    return this.mayBeCalled()
      .withAnyArguments();
  }
}

module.exports = Mock;