Source: jsmockMethod.js

/** @class MockMethod */
export default class MockMethod {
	/**
	 * @access private
	 **/
	constructor(obj, methodName) {
		this.excuteObjs = new Map();
		// {
		//      key:{
		//          arg:[],
		//          type:'function',
		//          excute : function(){}
		//      }
		// };
		this.record = { total: 0, param: {} };
		this.currentParam = this.transformParamToString([]);
		this.excuteObjs.set(this.currentParam, new Map());
		this.setup(obj, methodName);
	}

	/**
	 * @access private
	 **/
	transformParamToString(obj) {
		return JSON.stringify(obj);
	}

	/**
	 * @access private
	 **/
	setup(obj, methodName) {
		const that = this;
		const target = obj;
		target[methodName] = (...params) => {
			that.record.total++;
			const argString = that.transformParamToString(params);

			if (that.record.param[argString]) {
				that.record.param[argString] += 1;
			} else {
				that.record.param[argString] = 1;
			}

			const dataObj = that.excuteObjs.get(argString);

			if (dataObj) {
				const type = dataObj.get('type');
				const excute = dataObj.get('excute');
				if (type === 'function') {
					return excute.apply(dataObj, params);
				} else if (type === 'exception') {
					throw excute;
				} else if (type === 'return') {
					return excute;
				}
			} else {
				for (const [, info] of that.excuteObjs) {
					const arg = info.get('arg');
					if (
						arg && (arg.length === params.length) &&
						!arg.find((v, i) => (v !== params[i] && v !== '_js_mock_anything_param'))
						) {
						return info.get('excute');
					}
				}
			}
		};
	}
	/**
	 * set parameters of should_receive.
	 * If you use `with_param` that `and_xxx` method execute matching param.
	 * @instance
	 * @memberof MockMethod
	 * @param {...anything} params - list up parameters.
	 * @function with_param
	 * @returns {MockMethod}
	 * @example
mock("obj").should_receive("something").with_param(1,2).and_return("1");
// obj.something(1); => not return anything
// obj.something(1, 2); => "1"

// You can use `mock.anything`.
mock("obj").should_receive("something").with_param(1,mock.anything()).and_return("1");
// obj.something(1, 1); => "1"
// obj.something(1, 2); => "1"
// obj.something(1, 3); => "1"
	 **/
	with_param(...params) {
		this.currentParam = this.transformParamToString(params);
		this.excuteObjs.set(this.currentParam, (new Map()).set('arg', params));
		return this;
	}

	/**
	 * @access private
	 **/
	and_template(type, excute) {
		this.excuteObjs
			.get(this.currentParam)
			.set('type', type)
			.set('excute', excute);

		this.currentParam = this.transformParamToString([]);
	}
	/**
	 * set return value when should_receive called.
	 * @instance
	 * @memberof MockMethod
	 * @param {Object} returnVal - return value
	 * @function and_return
	 * @example
mock("obj").should_receive("something").and_return("1");
// obj.something(1); => "1"
// obj.something(1, 2); => "1"
	 **/
	and_return(returnVal) {
		this.and_template('return', returnVal);
	}
	/**
	 * Set execution function when should_receive called.
	 * @instance
	 * @memberof MockMethod
	 * @param {Function} returnFunction - execute function
	 * @function and_function
	 * @example
mock("obj").should_receive("something").and_function(function(){
	return "1";
});
// obj.something(1); => "1"
// obj.something(1, 2); => "1"
	 **/
	and_function(returnFunction) {
		this.and_template('function', returnFunction);
	}
	/**
	 * Set exception error when should_receive called.
	 * @instance
	 * @memberof MockMethod
	 * @param {String} returnException - exception message
	 * @function and_throw
	 * @example
mock("obj").should_receive("something").and_throw("error");
// obj.something(1); => new Error("error")
// obj.something(1, 2); => new Error("error")
	 **/
	and_throw(returnException) {
		this.and_template('exception', returnException);
	}

}