Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make all properties non-enumerable in spies, stubs, mocks and fakes #1975

Merged
merged 1 commit into from Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/sinon/behavior.js
Expand Up @@ -227,4 +227,4 @@ proto.createBehavior = createBehavior;

var asyncBehaviors = exportAsyncBehaviors(proto);

module.exports = extend({}, proto, asyncBehaviors);
module.exports = extend.nonEnum({}, proto, asyncBehaviors);
2 changes: 1 addition & 1 deletion lib/sinon/mock-expectation.js
Expand Up @@ -70,7 +70,7 @@ var mockExpectation = {
maxCalls: 1,

create: function create(methodName) {
var expectation = extend(stub.create(), mockExpectation);
var expectation = extend.nonEnum(stub.create(), mockExpectation);
delete expectation.create;
expectation.method = methodName;

Expand Down
5 changes: 2 additions & 3 deletions lib/sinon/mock.js
Expand Up @@ -47,8 +47,7 @@ extend(mock, {
throw new TypeError("object is null");
}

var mockObject = extend({}, mock);
mockObject.object = object;
var mockObject = extend.nonEnum({}, mock, { object: object });
delete mockObject.create;

return mockObject;
Expand Down Expand Up @@ -77,7 +76,7 @@ extend(mock, {
}

var expectation = mockExpectation.create(method);
extend(expectation, this.object[method]);
extend.nonEnum(expectation, this.object[method]);
push(this.expectations[method], expectation);
usePromiseLibrary(this.promiseLibrary, expectation);

Expand Down
48 changes: 27 additions & 21 deletions lib/sinon/spy.js
Expand Up @@ -152,7 +152,7 @@ function createProxy(func, proxyLength) {
return p.invoke(func, this, slice(arguments));
};
}
p.isSinonProxy = true;
extend.nonEnum(p, { isSinonProxy: true });
return p;
}

Expand All @@ -172,22 +172,25 @@ var spyApi = {
throw err;
}

this.called = false;
this.notCalled = true;
this.calledOnce = false;
this.calledTwice = false;
this.calledThrice = false;
this.callCount = 0;
this.firstCall = null;
this.secondCall = null;
this.thirdCall = null;
this.lastCall = null;
this.args = [];
this.returnValues = [];
this.thisValues = [];
this.exceptions = [];
this.callIds = [];
this.errorsWithCallStack = [];
extend.nonEnum(this, {
called: false,
notCalled: true,
calledOnce: false,
calledTwice: false,
calledThrice: false,
callCount: 0,
firstCall: null,
secondCall: null,
thirdCall: null,
lastCall: null,
args: [],
returnValues: [],
thisValues: [],
exceptions: [],
callIds: [],
errorsWithCallStack: []
});

if (this.fakes) {
forEach(this.fakes, function(fake) {
if (fake.resetHistory) {
Expand Down Expand Up @@ -222,10 +225,13 @@ var spyApi = {

proxy.resetHistory();
proxy.prototype = funk.prototype;
proxy.displayName = name || "spy";
proxy.toString = functionToString;
proxy.instantiateFake = spy.create;
proxy.id = "spy#" + uuid++;

extend.nonEnum(proxy, {
displayName: name || "spy",
toString: functionToString,
instantiateFake: spy.create,
id: "spy#" + uuid++
});

return proxy;
},
Expand Down
41 changes: 25 additions & 16 deletions lib/sinon/stub.js
Expand Up @@ -59,16 +59,19 @@ function stub(object, property) {
}

var s = stub.create(arity);
s.rootObj = object;
s.propName = property;
s.restore = function restore() {
if (actualDescriptor !== undefined) {
Object.defineProperty(object, property, actualDescriptor);
return;
}

delete object[property];
};
extend.nonEnum(s, {
rootObj: object,
propName: property,
restore: function restore() {
if (actualDescriptor !== undefined) {
Object.defineProperty(object, property, actualDescriptor);
return;
}

delete object[property];
}
});

return isStubbingNonFuncProperty ? s : wrapMethod(object, property, s);
}
Expand Down Expand Up @@ -129,16 +132,22 @@ var proto = {

var orig = functionStub;
functionStub = spy.create(functionStub, stubLength);
functionStub.id = "stub#" + uuid++;
functionStub.func = orig;

extend.nonEnum(functionStub, {
id: "stub#" + uuid++,
func: orig
});

extend(functionStub, stub);
functionStub.instantiateFake = stub.create;
functionStub.displayName = "stub";
functionStub.toString = functionToString;

functionStub.defaultBehavior = null;
functionStub.behaviors = [];
extend.nonEnum(functionStub, {
instantiateFake: stub.create,
displayName: "stub",
toString: functionToString,

defaultBehavior: null,
behaviors: []
});

return functionStub;
},
Expand Down
63 changes: 33 additions & 30 deletions lib/sinon/util/core/wrap-method.js
@@ -1,6 +1,7 @@
"use strict";

var getPropertyDescriptor = require("./get-property-descriptor");
var extend = require("./extend");
var hasOwnProperty = require("@sinonjs/commons").prototypes.object.hasOwnProperty;
var valueToString = require("@sinonjs/commons").valueToString;

Expand Down Expand Up @@ -105,40 +106,42 @@ module.exports = function wrapMethod(object, property, method) {
simplePropertyAssignment();
}

method.displayName = property;

// Set up an Error object for a stack trace which can be used later to find what line of
// code the original method was created on.
method.stackTraceError = new Error("Stack Trace for original");

method.restore = function() {
// For prototype properties try to reset by delete first.
// If this fails (ex: localStorage on mobile safari) then force a reset
// via direct assignment.
if (!owned) {
// In some cases `delete` may throw an error
try {
delete object[property];
} catch (e) {} // eslint-disable-line no-empty
// For native code functions `delete` fails without throwing an error
// on Chrome < 43, PhantomJS, etc.
} else if (hasES5Support) {
Object.defineProperty(object, property, wrappedMethodDesc);
}
extend.nonEnum(method, {
displayName: property,

if (hasES5Support) {
var descriptor = getPropertyDescriptor(object, property);
if (descriptor && descriptor.value === method) {
object[property] = wrappedMethod;
}
} else {
// Use strict equality comparison to check failures then force a reset
// Set up an Error object for a stack trace which can be used later to find what line of
// code the original method was created on.
stackTraceError: new Error("Stack Trace for original"),

restore: function() {
// For prototype properties try to reset by delete first.
// If this fails (ex: localStorage on mobile safari) then force a reset
// via direct assignment.
if (object[property] === method) {
object[property] = wrappedMethod;
if (!owned) {
// In some cases `delete` may throw an error
try {
delete object[property];
} catch (e) {} // eslint-disable-line no-empty
// For native code functions `delete` fails without throwing an error
// on Chrome < 43, PhantomJS, etc.
} else if (hasES5Support) {
Object.defineProperty(object, property, wrappedMethodDesc);
}

if (hasES5Support) {
var descriptor = getPropertyDescriptor(object, property);
if (descriptor && descriptor.value === method) {
object[property] = wrappedMethod;
}
} else {
// Use strict equality comparison to check failures then force a reset
// via direct assignment.
if (object[property] === method) {
object[property] = wrappedMethod;
}
}
}
};
});

method.wrappedMethod = wrappedMethod;

Expand Down
2 changes: 1 addition & 1 deletion lib/sinon/util/fake-timers.js
Expand Up @@ -36,7 +36,7 @@ exports.useFakeTimers = function(dateOrConfig) {
}

if (argumentIsObject) {
var config = extend({}, dateOrConfig);
var config = extend.nonEnum({}, dateOrConfig);
var globalCtx = config.global;
delete config.global;
return createClock(config, globalCtx);
Expand Down