Skip to content

Commit

Permalink
Merge pull request #1975 from ehmicky/master
Browse files Browse the repository at this point in the history
Make all properties non-enumerable in spies, stubs, mocks and fakes
  • Loading branch information
fatso83 authored Feb 18, 2019
2 parents 894951c + c1d9625 commit fc2a32a
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 73 deletions.
2 changes: 1 addition & 1 deletion lib/sinon/behavior.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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

0 comments on commit fc2a32a

Please sign in to comment.