mirror of
https://github.com/github/codeql-action.git
synced 2026-01-06 22:50:17 +08:00
* --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm - dependency-name: sinon dependency-type: direct:development update-type: version-update:semver-major dependency-group: npm ... Signed-off-by: dependabot[bot] <support@github.com> * Update checked-in dependencies --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
539 lines
15 KiB
JavaScript
539 lines
15 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 throwOnAccessors(descriptor) {
|
|
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");
|
|
}
|
|
}
|
|
|
|
function verifySameType(object, property, replacement) {
|
|
if (typeof object[property] !== typeof replacement) {
|
|
throw new TypeError(
|
|
`Cannot replace ${typeof object[
|
|
property
|
|
]} with ${typeof replacement}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function checkForValidArguments(descriptor, property, replacement) {
|
|
if (typeof descriptor === "undefined") {
|
|
throw new TypeError(
|
|
`Cannot replace non-existent property ${valueToString(
|
|
property,
|
|
)}. Perhaps you meant sandbox.define()?`,
|
|
);
|
|
}
|
|
|
|
if (typeof replacement === "undefined") {
|
|
throw new TypeError("Expected replacement argument to be defined");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A sinon sandbox
|
|
*
|
|
* @param opts
|
|
* @param {object} [opts.assertOptions] see the CreateAssertOptions in ./assert
|
|
* @class
|
|
*/
|
|
function Sandbox(opts = {}) {
|
|
const sandbox = this;
|
|
const assertOptions = opts.assertOptions || {};
|
|
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(assertOptions);
|
|
|
|
sandbox.serverPrototype = fakeServer;
|
|
|
|
// this is for testing only
|
|
sandbox.getFakes = function getFakes() {
|
|
return collection;
|
|
};
|
|
|
|
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.define = function () {
|
|
return sandbox.define.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, 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() {
|
|
if (!sandbox.injectedKeys) {
|
|
return;
|
|
}
|
|
|
|
forEach(sandbox.injectedKeys, function (injectedKey) {
|
|
delete sandbox.injectInto[injectedKey];
|
|
});
|
|
|
|
sandbox.injectedKeys.length = 0;
|
|
};
|
|
|
|
/**
|
|
* Creates a restorer function for the property
|
|
*
|
|
* @param {object|Function} object
|
|
* @param {string} property
|
|
* @param {boolean} forceAssignment
|
|
* @returns {Function} restorer function
|
|
*/
|
|
function getFakeRestorer(object, property, forceAssignment = false) {
|
|
const descriptor = getPropertyDescriptor(object, property);
|
|
const value = forceAssignment && object[property];
|
|
|
|
function restorer() {
|
|
if (forceAssignment) {
|
|
object[property] = value;
|
|
} else 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`,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Replace an existing property
|
|
*
|
|
* @param {object|Function} object
|
|
* @param {string} property
|
|
* @param {*} replacement a fake, stub, spy or any other value
|
|
* @returns {*}
|
|
*/
|
|
sandbox.replace = function replace(object, property, replacement) {
|
|
const descriptor = getPropertyDescriptor(object, property);
|
|
checkForValidArguments(descriptor, property, replacement);
|
|
throwOnAccessors(descriptor);
|
|
verifySameType(object, property, replacement);
|
|
|
|
verifyNotReplaced(object, property);
|
|
|
|
// store a function for restoring the replaced property
|
|
push(fakeRestorers, getFakeRestorer(object, property));
|
|
|
|
object[property] = replacement;
|
|
|
|
return replacement;
|
|
};
|
|
|
|
sandbox.replace.usingAccessor = function replaceUsingAccessor(
|
|
object,
|
|
property,
|
|
replacement,
|
|
) {
|
|
const descriptor = getPropertyDescriptor(object, property);
|
|
checkForValidArguments(descriptor, property, replacement);
|
|
verifySameType(object, property, replacement);
|
|
|
|
verifyNotReplaced(object, property);
|
|
|
|
// store a function for restoring the replaced property
|
|
push(fakeRestorers, getFakeRestorer(object, property, true));
|
|
|
|
object[property] = replacement;
|
|
|
|
return replacement;
|
|
};
|
|
|
|
sandbox.define = function define(object, property, value) {
|
|
const descriptor = getPropertyDescriptor(object, property);
|
|
|
|
if (descriptor) {
|
|
throw new TypeError(
|
|
`Cannot define the already existing property ${valueToString(
|
|
property,
|
|
)}. Perhaps you meant sandbox.replace()?`,
|
|
);
|
|
}
|
|
|
|
if (typeof value === "undefined") {
|
|
throw new TypeError("Expected value argument to be defined");
|
|
}
|
|
|
|
verifyNotReplaced(object, property);
|
|
|
|
// store a function for restoring the defined property
|
|
push(fakeRestorers, getFakeRestorer(object, property));
|
|
|
|
object[property] = value;
|
|
|
|
return value;
|
|
};
|
|
|
|
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, property, types] = args;
|
|
|
|
const isSpyingOnEntireObject =
|
|
typeof property === "undefined" && typeof object === "object";
|
|
|
|
if (isSpyingOnEntireObject) {
|
|
const ownMethods = collectOwnMethods(spy);
|
|
|
|
forEach(ownMethods, function (method) {
|
|
addToCollection(method);
|
|
});
|
|
|
|
usePromiseLibrary(promiseLib, ownMethods);
|
|
} else if (Array.isArray(types)) {
|
|
for (const accessorType of types) {
|
|
addToCollection(spy[accessorType]);
|
|
usePromiseLibrary(promiseLib, spy[accessorType]);
|
|
}
|
|
} 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;
|