helpyourneighbour/node_modules/sinon/lib/sinon/sandbox.js
BibaBot bfd432d094
Some checks are pending
Docker Test / test (push) Waiting to run
Add comprehensive tests for role middleware and fix package dependencies
2026-03-16 20:07:22 +00:00

473 lines
13 KiB
JavaScript

"use strict";
const arrayProto = require("@sinonjs/commons").prototypes.array;
const logger = require("@sinonjs/commons").deprecated;
const collectOwnMethods = require("./collect-own-methods");
const getPropertyDescriptor = require("./util/core/get-property-descriptor");
const isPropertyConfigurable = require("./util/core/is-property-configurable");
const match = require("@sinonjs/samsam").createMatcher;
const sinonAssert = require("./assert");
const sinonClock = require("./util/fake-timers");
const sinonMock = require("./mock");
const sinonSpy = require("./spy");
const sinonStub = require("./stub");
const sinonCreateStubInstance = require("./create-stub-instance");
const sinonFake = require("./fake");
const valueToString = require("@sinonjs/commons").valueToString;
const fakeServer = require("nise").fakeServer;
const fakeXhr = require("nise").fakeXhr;
const usePromiseLibrary = require("./util/core/use-promise-library");
const DEFAULT_LEAK_THRESHOLD = 10000;
const filter = arrayProto.filter;
const forEach = arrayProto.forEach;
const push = arrayProto.push;
const reverse = arrayProto.reverse;
function applyOnEach(fakes, method) {
const matchingFakes = filter(fakes, function (fake) {
return typeof fake[method] === "function";
});
forEach(matchingFakes, function (fake) {
fake[method]();
});
}
function Sandbox() {
const sandbox = this;
let fakeRestorers = [];
let promiseLib;
let collection = [];
let loggedLeakWarning = false;
sandbox.leakThreshold = DEFAULT_LEAK_THRESHOLD;
function addToCollection(object) {
if (
push(collection, object) > sandbox.leakThreshold &&
!loggedLeakWarning
) {
// eslint-disable-next-line no-console
logger.printWarning(
"Potential memory leak detected; be sure to call restore() to clean up your sandbox. To suppress this warning, modify the leakThreshold property of your sandbox."
);
loggedLeakWarning = true;
}
}
sandbox.assert = sinonAssert.createAssertObject();
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() {
const stubbed = sinonCreateStubInstance.apply(null, arguments);
const ownMethods = collectOwnMethods(stubbed);
forEach(ownMethods, function (method) {
addToCollection(method);
});
usePromiseLibrary(promiseLib, ownMethods);
return stubbed;
};
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);
};
obj.createStubInstance = function () {
return sandbox.createStubInstance.apply(sandbox, arguments);
};
obj.fake = function () {
return sandbox.fake.apply(null, arguments);
};
obj.replace = function () {
return sandbox.replace.apply(null, arguments);
};
obj.replaceSetter = function () {
return sandbox.replaceSetter.apply(null, arguments);
};
obj.replaceGetter = function () {
return sandbox.replaceGetter.apply(null, arguments);
};
if (sandbox.clock) {
obj.clock = sandbox.clock;
}
if (sandbox.server) {
obj.server = sandbox.server;
obj.requests = sandbox.server.requests;
}
obj.match = match;
return obj;
};
sandbox.mock = function mock() {
const m = sinonMock.apply(null, arguments);
addToCollection(m);
usePromiseLibrary(promiseLib, 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) {
const method = f.resetHistory || f.reset;
if (method) {
method.call(f);
}
}
forEach(collection, function (fake) {
if (typeof fake === "function") {
privateResetHistory(fake);
return;
}
const methods = [];
if (fake.get) {
push(methods, fake.get);
}
if (fake.set) {
push(methods, fake.set);
}
forEach(methods, privateResetHistory);
});
};
sandbox.restore = function restore() {
if (arguments.length) {
throw new Error(
"sandbox.restore() does not take any parameters. Perhaps you meant stub.restore()"
);
}
reverse(collection);
applyOnEach(collection, "restore");
collection = [];
forEach(fakeRestorers, function (restorer) {
restorer();
});
fakeRestorers = [];
sandbox.restoreContext();
};
sandbox.restoreContext = function restoreContext() {
let injectedKeys = sandbox.injectedKeys;
const injectInto = sandbox.injectInto;
if (!injectedKeys) {
return;
}
forEach(injectedKeys, function (injectedKey) {
delete injectInto[injectedKey];
});
injectedKeys = [];
};
function getFakeRestorer(object, property) {
const descriptor = getPropertyDescriptor(object, property);
function restorer() {
if (descriptor.isOwn) {
Object.defineProperty(object, property, descriptor);
} else {
delete object[property];
}
}
restorer.object = object;
restorer.property = property;
return restorer;
}
function verifyNotReplaced(object, property) {
forEach(fakeRestorers, 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) {
const descriptor = getPropertyDescriptor(object, property);
if (typeof descriptor === "undefined") {
throw new TypeError(
`Cannot replace non-existent 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(fakeRestorers, getFakeRestorer(object, property));
object[property] = replacement;
return replacement;
};
sandbox.replaceGetter = function replaceGetter(
object,
property,
replacement
) {
const descriptor = getPropertyDescriptor(object, property);
if (typeof descriptor === "undefined") {
throw new TypeError(
`Cannot replace non-existent 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(fakeRestorers, getFakeRestorer(object, property));
Object.defineProperty(object, property, {
get: replacement,
configurable: isPropertyConfigurable(object, property),
});
return replacement;
};
sandbox.replaceSetter = function replaceSetter(
object,
property,
replacement
) {
const descriptor = getPropertyDescriptor(object, property);
if (typeof descriptor === "undefined") {
throw new TypeError(
`Cannot replace non-existent 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(fakeRestorers, getFakeRestorer(object, property));
// eslint-disable-next-line accessor-pairs
Object.defineProperty(object, property, {
set: replacement,
configurable: isPropertyConfigurable(object, property),
});
return replacement;
};
function commonPostInitSetup(args, spy) {
const object = args[0];
const property = args[1];
const isSpyingOnEntireObject =
typeof property === "undefined" && typeof object === "object";
if (isSpyingOnEntireObject) {
const ownMethods = collectOwnMethods(spy);
forEach(ownMethods, function (method) {
addToCollection(method);
});
usePromiseLibrary(promiseLib, ownMethods);
} else {
addToCollection(spy);
usePromiseLibrary(promiseLib, spy);
}
return spy;
}
sandbox.spy = function spy() {
const createdSpy = sinonSpy.apply(sinonSpy, arguments);
return commonPostInitSetup(arguments, createdSpy);
};
sandbox.stub = function stub() {
const createdStub = sinonStub.apply(sinonStub, arguments);
return commonPostInitSetup(arguments, createdStub);
};
// eslint-disable-next-line no-unused-vars
sandbox.fake = function fake(f) {
const s = sinonFake.apply(sinonFake, arguments);
addToCollection(s);
return s;
};
forEach(Object.keys(sinonFake), function (key) {
const fakeBehavior = sinonFake[key];
if (typeof fakeBehavior === "function") {
sandbox.fake[key] = function () {
const s = fakeBehavior.apply(fakeBehavior, arguments);
addToCollection(s);
return s;
};
}
});
sandbox.useFakeTimers = function useFakeTimers(args) {
const clock = sinonClock.useFakeTimers.call(null, args);
sandbox.clock = clock;
addToCollection(clock);
return clock;
};
sandbox.verify = function verify() {
applyOnEach(collection, "verify");
};
sandbox.verifyAndRestore = function verifyAndRestore() {
let exception;
try {
sandbox.verify();
} catch (e) {
exception = e;
}
sandbox.restore();
if (exception) {
throw exception;
}
};
sandbox.useFakeServer = function useFakeServer() {
const proto = sandbox.serverPrototype || fakeServer;
if (!proto || !proto.create) {
return null;
}
sandbox.server = proto.create();
addToCollection(sandbox.server);
return sandbox.server;
};
sandbox.useFakeXMLHttpRequest = function useFakeXMLHttpRequest() {
const xhr = fakeXhr.useFakeXMLHttpRequest();
addToCollection(xhr);
return xhr;
};
sandbox.usingPromise = function usingPromise(promiseLibrary) {
promiseLib = promiseLibrary;
collection.promiseLibrary = promiseLibrary;
return sandbox;
};
}
Sandbox.prototype.match = match;
module.exports = Sandbox;