Skip to content

Commit

Permalink
Set wrappedMethod on getters/setters
Browse files Browse the repository at this point in the history
  • Loading branch information
rgroothuijsen authored and fatso83 committed May 25, 2021
1 parent 64b96d7 commit 26edac8
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 26 deletions.
98 changes: 74 additions & 24 deletions lib/sinon/util/core/wrap-method.js
Expand Up @@ -5,6 +5,7 @@ var extend = require("./extend");
var hasOwnProperty =
require("@sinonjs/commons").prototypes.object.hasOwnProperty;
var valueToString = require("@sinonjs/commons").valueToString;
var push = require("@sinonjs/commons").prototypes.array.push;

function isFunction(obj) {
return (
Expand All @@ -21,6 +22,18 @@ function mirrorProperties(target, source) {
}
}

function getAccessor(object, property, method) {
var accessors = ["get", "set"];
var descriptor = getPropertyDescriptor(object, property);

for (var i = 0; i < accessors.length; i++) {
if (descriptor[accessors[i]] && descriptor[accessors[i]].name === method.name) {
return accessors[i];
}
}
return null;
}

// Cheap way to detect if we have ES5 support.
var hasES5Support = "keys" in Object;

Expand Down Expand Up @@ -67,7 +80,9 @@ module.exports = function wrapMethod(object, property, method) {
}
}

var error, wrappedMethod, i, wrappedMethodDesc;
var error, wrappedMethods, wrappedMethod, i, wrappedMethodDesc, target, accessor;

wrappedMethods = [];

function simplePropertyAssignment() {
wrappedMethod = object[property];
Expand Down Expand Up @@ -109,6 +124,7 @@ module.exports = function wrapMethod(object, property, method) {
for (i = 0; i < types.length; i++) {
wrappedMethod = wrappedMethodDesc[types[i]];
checkWrappedMethod(wrappedMethod);
push(wrappedMethods, wrappedMethod);
}

mirrorProperties(methodDesc, wrappedMethodDesc);
Expand All @@ -129,49 +145,83 @@ module.exports = function wrapMethod(object, property, method) {
simplePropertyAssignment();
}

extend.nonEnum(method, {
displayName: property,
extendObjectWithWrappedMethods();

function extendObjectWithWrappedMethods() {
for (i = 0; i < wrappedMethods.length; i++) {
accessor = getAccessor(object, property, wrappedMethods[i]);
target = accessor ? method[accessor] : method;
extend.nonEnum(target, {
displayName: property,
wrappedMethod: wrappedMethods[i],

wrappedMethod: wrappedMethod,
// 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"),

// 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: restore
});

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.
target.restore.sinon = true;
if (!hasES5Support) {
mirrorProperties(target, wrappedMethod);
}
}
}

function restore() {
accessor = getAccessor(object, property, this.wrappedMethod);
var descriptor;
// 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 (accessor) {
if (!owned) {
// In some cases `delete` may throw an error
try {
delete object[property];
// In some cases `delete` may throw an error
delete object[property][accessor];
} 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);
descriptor = getPropertyDescriptor(object, property);
descriptor[accessor] = wrappedMethodDesc[accessor];
Object.defineProperty(object, property, descriptor);
}

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

method.restore.sinon = true;
} else {
if (!owned) {
try {
delete object[property];
} catch (e) {} // eslint-disable-line no-empty
} else if (hasES5Support) {
Object.defineProperty(object, property, wrappedMethodDesc);
}

if (!hasES5Support) {
mirrorProperties(method, wrappedMethod);
if (hasES5Support) {
descriptor = getPropertyDescriptor(object, property);
if (descriptor && descriptor.value === target) {
object[property] = this.wrappedMethod;
}
} else {
if (object[property] === target) {
object[property] = this.wrappedMethod;
}
}
}
}

return method;
Expand Down
34 changes: 32 additions & 2 deletions test/spy-test.js
Expand Up @@ -329,8 +329,38 @@ describe("spy", function () {
assert(spy.get.calledOnce);
});

describe("global.Error", function () {
beforeEach(function () {
it("sets wrappedMethod on getter and setter", function() {
var object = {
get test() {
return this.property;
},
set test(value) {
this.property = value;
}
};

var descriptor1 = Object.getOwnPropertyDescriptor(object, "test");
var spy = createSpy(object, "test", ["get", "set"]);
var descriptor2 = Object.getOwnPropertyDescriptor(object, "test");

refute.equals(descriptor1, descriptor2);

refute.isUndefined(spy.get.wrappedMethod);
refute.isUndefined(spy.get.restore);
refute.isUndefined(spy.set.wrappedMethod);
refute.isUndefined(spy.set.restore);
assert.isUndefined(spy.wrappedMethod);
assert.isUndefined(spy.restore);

spy.get.restore();
spy.set.restore();

var descriptor3 = Object.getOwnPropertyDescriptor(object, "test");
assert.equals(descriptor1, descriptor3);
});

describe("global.Error", function() {
beforeEach(function() {
this.originalError = globalContext.Error;
});

Expand Down

0 comments on commit 26edac8

Please sign in to comment.