UNPKG

sinon

Version:

JavaScript test spies, stubs and mocks.

365 lines (274 loc) 10.7 kB
"use strict"; var collectOwnMethods = require("./collect-own-methods"); var getPropertyDescriptor = require("./util/core/get-property-descriptor"); var isEsModule = require("./util/core/is-es-module"); var isPropertyConfigurable = require("./util/core/is-property-configurable"); var isNonExistentOwnProperty = require("./util/core/is-non-existent-own-property"); var sinonMatch = require("./match"); var sinonAssert = require("./assert"); var sinonClock = require("./util/fake_timers"); var sinonMock = require("./mock"); var sinonSpy = require("./spy"); var sinonStub = require("./stub"); var valueToString = require("./util/core/value-to-string"); var fakeServer = require("nise").fakeServer; var fakeXhr = require("nise").fakeXhr; var usePromiseLibrary = require("./util/core/use-promise-library"); // cache original versions, to prevent issues when they are stubbed in user space var push = Array.prototype.push; var filter = Array.prototype.filter; var forEach = Array.prototype.filter; function applyOnEach(fakes, method) { var matchingFakes = filter.call(fakes, function (fake) { return typeof fake[method] === "function"; }); forEach.call(matchingFakes, function (fake) { fake[method](); }); } function Sandbox() { var sandbox = this; var collection = []; var fakeRestorers = []; var promiseLib; sandbox.serverPrototype = fakeServer; // this is for testing only sandbox.getFakes = function getFakes() { return collection; }; // this is for testing only sandbox.getRestorers = function () { return fakeRestorers; }; sandbox.createStubInstance = function createStubInstance(constructor) { if (typeof constructor !== "function") { throw new TypeError("The constructor should be a function."); } return this.stub.call(this, Object.create(constructor.prototype)); }; sandbox.inject = function inject(obj) { obj.spy = function () { return sandbox.spy.apply(null, arguments); }; obj.stub = function () { return sandbox.stub.apply(null, arguments); }; obj.mock = function () { return sandbox.mock.apply(null, arguments); }; if (sandbox.clock) { obj.clock = sandbox.clock; } if (sandbox.server) { obj.server = sandbox.server; obj.requests = sandbox.server.requests; } obj.match = sinonMatch; return obj; }; sandbox.mock = function mock() { var m = sinonMock.apply(null, arguments); push.call(collection, m); return m; }; sandbox.reset = function reset() { applyOnEach(collection, "reset"); applyOnEach(collection, "resetHistory"); }; sandbox.resetBehavior = function resetBehavior() { applyOnEach(collection, "resetBehavior"); }; sandbox.resetHistory = function resetHistory() { function privateResetHistory(f) { var method = f.resetHistory || f.reset; if (method) { method.call(f); } } forEach.call(collection, function (fake) { if (typeof fake === "function") { privateResetHistory(fake); return; } var methods = []; if (fake.get) { methods.push(fake.get); } if (fake.set) { methods.push(fake.set); } methods.forEach(privateResetHistory); }); }; sandbox.restore = function restore() { if (arguments.length) { throw new Error("sandbox.restore() does not take any parameters. Perhaps you meant stub.restore()"); } applyOnEach(collection, "restore"); collection = []; forEach.call(fakeRestorers, function (restorer) { restorer(); }); fakeRestorers = []; sandbox.restoreContext(); }; sandbox.restoreContext = function restoreContext() { var injectedKeys = sandbox.injectedKeys; var injectInto = sandbox.injectInto; if (!injectedKeys) { return; } injectedKeys.forEach(function (injectedKey) { delete injectInto[injectedKey]; }); injectedKeys = []; }; function getFakeRestorer(object, property) { var descriptor = getPropertyDescriptor(object, property); function restorer() { Object.defineProperty(object, property, descriptor); } restorer.object = object; restorer.property = property; return restorer; } function verifyNotReplaced(object, property) { fakeRestorers.forEach(function (fakeRestorer) { if (fakeRestorer.object === object && fakeRestorer.property === property) { throw new TypeError("Attempted to replace " + property + " which is already replaced"); } }); } sandbox.replace = function replace(object, property, replacement) { var descriptor = getPropertyDescriptor(object, property); if (typeof descriptor === "undefined") { throw new TypeError("Cannot replace non-existent own property " + valueToString(property)); } if (typeof replacement === "undefined") { throw new TypeError("Expected replacement argument to be defined"); } if (typeof descriptor.get === "function") { throw new Error("Use sandbox.replaceGetter for replacing getters"); } if (typeof descriptor.set === "function") { throw new Error("Use sandbox.replaceSetter for replacing setters"); } if (typeof object[property] !== typeof replacement) { throw new TypeError("Cannot replace " + typeof object[property] + " with " + typeof replacement); } verifyNotReplaced(object, property); // store a function for restoring the replaced property push.call(fakeRestorers, getFakeRestorer(object, property)); object[property] = replacement; return replacement; }; sandbox.replaceGetter = function replaceGetter(object, property, replacement) { var descriptor = getPropertyDescriptor(object, property); if (typeof descriptor === "undefined") { throw new TypeError("Cannot replace non-existent own property " + valueToString(property)); } if (typeof replacement !== "function") { throw new TypeError("Expected replacement argument to be a function"); } if (typeof descriptor.get !== "function") { throw new Error("`object.property` is not a getter"); } verifyNotReplaced(object, property); // store a function for restoring the replaced property push.call(fakeRestorers, getFakeRestorer(object, property)); Object.defineProperty(object, property, { get: replacement, configurable: isPropertyConfigurable(object, property) }); return replacement; }; sandbox.replaceSetter = function replaceSetter(object, property, replacement) { var descriptor = getPropertyDescriptor(object, property); if (typeof descriptor === "undefined") { throw new TypeError("Cannot replace non-existent own property " + valueToString(property)); } if (typeof replacement !== "function") { throw new TypeError("Expected replacement argument to be a function"); } if (typeof descriptor.set !== "function") { throw new Error("`object.property` is not a setter"); } verifyNotReplaced(object, property); // store a function for restoring the replaced property push.call(fakeRestorers, getFakeRestorer(object, property)); // eslint-disable-next-line accessor-pairs Object.defineProperty(object, property, { set: replacement, configurable: isPropertyConfigurable(object, property) }); return replacement; }; sandbox.spy = function spy() { var s = sinonSpy.apply(sinonSpy, arguments); push.call(collection, s); return s; }; sandbox.stub = function stub(object, property) { if (isEsModule(object)) { throw new TypeError("ES Modules cannot be stubbed"); } if (isNonExistentOwnProperty(object, property)) { throw new TypeError("Cannot stub non-existent own property " + valueToString(property)); } var stubbed = sinonStub.apply(null, arguments); var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object"; if (isStubbingEntireObject) { var ownMethods = collectOwnMethods(stubbed); ownMethods.forEach(function (method) { push.call(collection, method); }); usePromiseLibrary(promiseLib, ownMethods); } else { push.call(collection, stubbed); usePromiseLibrary(promiseLib, stubbed); } return stubbed; }; sandbox.useFakeTimers = function useFakeTimers(args) { var clock = sinonClock.useFakeTimers.call(null, args); sandbox.clock = clock; push.call(collection, clock); return clock; }; sandbox.verify = function verify() { applyOnEach(collection, "verify"); }; sandbox.verifyAndRestore = function verifyAndRestore() { var exception; try { sandbox.verify(); } catch (e) { exception = e; } sandbox.restore(); if (exception) { throw exception; } }; sandbox.useFakeServer = function useFakeServer() { var proto = sandbox.serverPrototype || fakeServer; if (!proto || !proto.create) { return null; } sandbox.server = proto.create(); push.call(collection, sandbox.server); return sandbox.server; }; sandbox.useFakeXMLHttpRequest = function useFakeXMLHttpRequest() { var xhr = fakeXhr.useFakeXMLHttpRequest(); return push.call(collection, xhr); }; sandbox.usingPromise = function usingPromise(promiseLibrary) { promiseLib = promiseLibrary; collection.promiseLibrary = promiseLibrary; return sandbox; }; } Sandbox.prototype.assert = sinonAssert; Sandbox.prototype.match = sinonMatch; module.exports = Sandbox;