diff --git a/.eslintignore b/.eslintignore index 72f4ebe2e..f72718b78 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ coverage/ +out/ pkg/ tmp/ docs/_site/ diff --git a/.gitignore b/.gitignore index 40ab5d5dd..e91f0cf93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +out/ pkg tmp/ node_modules diff --git a/.prettierignore b/.prettierignore index f36c7a9d0..cca2dc3a9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ coverage/ +out/ pkg/ tmp/ .sass-cache diff --git a/lib/sinon/fake.js b/lib/sinon/fake.js index 5e4b7d8f4..e90724b9e 100644 --- a/lib/sinon/fake.js +++ b/lib/sinon/fake.js @@ -5,117 +5,283 @@ var createProxy = require("./proxy"); var nextTick = require("./util/core/next-tick"); var slice = arrayProto.slice; +var promiseLib = Promise; -function getError(value) { - return value instanceof Error ? value : new Error(value); -} - -var uuid = 0; -function wrapFunc(f) { - var proxy; - var fakeInstance = function () { - var firstArg, lastArg; +module.exports = fake; - if (arguments.length > 0) { - firstArg = arguments[0]; - lastArg = arguments[arguments.length - 1]; - } +/** + * Returns a `fake` that records all calls, arguments and return values. + * + * When an `f` argument is supplied, this implementation will be used. + * + * @example + * // create an empty fake + * var f1 = sinon.fake(); + * + * f1(); + * + * f1.calledOnce() + * // true + * + * @example + * function greet(greeting) { + * console.log(`Hello ${greeting}`); + * } + * + * // create a fake with implementation + * var f2 = sinon.fake(greet); + * + * // Hello world + * f2("world"); + * + * f2.calledWith("world"); + * // true + * + * @param {Function|undefined} f + * @returns {Function} + * @namespace + */ +function fake(f) { + if (arguments.length > 0 && typeof f !== "function") { + throw new TypeError("Expected f argument to be a Function"); + } - var callback = - lastArg && typeof lastArg === "function" ? lastArg : undefined; + return wrapFunc(f); +} - proxy.firstArg = firstArg; - proxy.lastArg = lastArg; - proxy.callback = callback; +/** + * Creates a `fake` that returns the provided `value`, as well as recording all + * calls, arguments and return values. + * + * @example + * var f1 = sinon.fake.returns(42); + * + * f1(); + * // 42 + * + * @memberof fake + * @param {*} value + * @returns {Function} + */ +fake.returns = function returns(value) { + // eslint-disable-next-line jsdoc/require-jsdoc + function f() { + return value; + } - return f && f.apply(this, arguments); - }; - proxy = createProxy(fakeInstance, f || fakeInstance); + return wrapFunc(f); +}; - proxy.displayName = "fake"; - proxy.id = `fake#${uuid++}`; +/** + * Creates a `fake` that throws an Error. + * If the `value` argument does not have Error in its prototype chain, it will + * be used for creating a new error. + * + * @example + * var f1 = sinon.fake.throws("hello"); + * + * f1(); + * // Uncaught Error: hello + * + * @example + * var f2 = sinon.fake.throws(new TypeError("Invalid argument")); + * + * f2(); + * // Uncaught TypeError: Invalid argument + * + * @memberof fake + * @param {*|Error} value + * @returns {Function} + */ +fake.throws = function throws(value) { + // eslint-disable-next-line jsdoc/require-jsdoc + function f() { + throw getError(value); + } - return proxy; -} + return wrapFunc(f); +}; -function fakeClass() { - var promiseLib = null; - if (typeof Promise === "function") { - promiseLib = Promise; +/** + * Creates a `fake` that returns a promise that resolves to the passed `value` + * argument. + * + * @example + * var f1 = sinon.fake.resolves("apple pie"); + * + * await f1(); + * // "apple pie" + * + * @memberof fake + * @param {*} value + * @returns {Function} + */ +fake.resolves = function resolves(value) { + // eslint-disable-next-line jsdoc/require-jsdoc + function f() { + return promiseLib.resolve(value); } - function fake(f) { - if (arguments.length > 0 && typeof f !== "function") { - throw new TypeError("Expected f argument to be a Function"); - } + return wrapFunc(f); +}; - return wrapFunc(f); +/** + * Creates a `fake` that returns a promise that rejects to the passed `value` + * argument. When `value` does not have Error in its prototype chain, it will be + * wrapped in an Error. + * + * @example + * var f1 = sinon.fake.rejects(":("); + * + * try { + * await ft(); + * } catch (error) { + * console.log(error); + * // ":(" + * } + * + * @memberof fake + * @param {*} value + * @returns {Function} + */ +fake.rejects = function rejects(value) { + // eslint-disable-next-line jsdoc/require-jsdoc + function f() { + return promiseLib.reject(getError(value)); } - fake.returns = function returns(value) { - function f() { - return value; - } + return wrapFunc(f); +}; - return wrapFunc(f); - }; +/** + * Causes `fake` to use a custom Promise implementation, instead of the native + * Promise implementation. + * + * @example + * const bluebird = require("bluebird"); + * sinon.fake.usingPromise(bluebird); + * + * @memberof fake + * @param {*} promiseLibrary + * @returns {Function} + */ +fake.usingPromise = function usingPromise(promiseLibrary) { + promiseLib = promiseLibrary; + return fake; +}; - fake.throws = function throws(value) { - function f() { - throw getError(value); +/** + * Returns a `fake` that calls the callback with the defined arguments. + * + * @example + * function callback() { + * console.log(arguments.join("*")); + * } + * + * const f1 = sinon.fake.yields("apple", "pie"); + * + * f1(callback); + * // "apple*pie" + * + * @memberof fake + * @returns {Function} + */ +fake.yields = function yields() { + var values = slice(arguments); + + // eslint-disable-next-line jsdoc/require-jsdoc + function f() { + var callback = arguments[arguments.length - 1]; + if (typeof callback !== "function") { + throw new TypeError("Expected last argument to be a function"); } - return wrapFunc(f); - }; + callback.apply(null, values); + } - fake.resolves = function resolves(value) { - function f() { - return promiseLib.resolve(value); - } + return wrapFunc(f); +}; - return wrapFunc(f); - }; +/** + * Returns a `fake` that calls the callback **asynchronously** with the + * defined arguments. + * + * @example + * function callback() { + * console.log(arguments.join("*")); + * } + * + * const f1 = sinon.fake.yields("apple", "pie"); + * + * f1(callback); + * + * setTimeout(() => { + * // "apple*pie" + * }); + * + * @memberof fake + * @returns {Function} + */ +fake.yieldsAsync = function yieldsAsync() { + var values = slice(arguments); - fake.rejects = function rejects(value) { - function f() { - return promiseLib.reject(getError(value)); + // eslint-disable-next-line jsdoc/require-jsdoc + function f() { + var callback = arguments[arguments.length - 1]; + if (typeof callback !== "function") { + throw new TypeError("Expected last argument to be a function"); } + nextTick(function () { + callback.apply(null, values); + }); + } - return wrapFunc(f); - }; + return wrapFunc(f); +}; - fake.usingPromise = function usingPromise(promiseLibrary) { - promiseLib = promiseLibrary; - return fake; - }; +var uuid = 0; +/** + * Creates a proxy (sinon concept) from the passed function. + * + * @private + * @param {Function} f + * @returns {Function} + */ +function wrapFunc(f) { + var proxy; + var fakeInstance = function () { + var firstArg, lastArg; - function yieldInternal(async, values) { - function f() { - var callback = arguments[arguments.length - 1]; - if (typeof callback !== "function") { - throw new TypeError("Expected last argument to be a function"); - } - if (async) { - nextTick(function () { - callback.apply(null, values); - }); - } else { - callback.apply(null, values); - } + if (arguments.length > 0) { + firstArg = arguments[0]; + lastArg = arguments[arguments.length - 1]; } - return wrapFunc(f); - } + var callback = + lastArg && typeof lastArg === "function" ? lastArg : undefined; - fake.yields = function yields() { - return yieldInternal(false, slice(arguments)); - }; + proxy.firstArg = firstArg; + proxy.lastArg = lastArg; + proxy.callback = callback; - fake.yieldsAsync = function yieldsAsync() { - return yieldInternal(true, slice(arguments)); + return f && f.apply(this, arguments); }; + proxy = createProxy(fakeInstance, f || fakeInstance); - return fake; + proxy.displayName = "fake"; + proxy.id = `fake#${uuid++}`; + + return proxy; } -module.exports = fakeClass(); +/** + * Returns an Error instance from the passed value, if the value is not + * already an Error instance. + * + * @private + * @param {*} value [description] + * @returns {Error} [description] + */ +function getError(value) { + return value instanceof Error ? value : new Error(value); +}