diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..1ad899dbc Binary files /dev/null and b/.DS_Store differ diff --git a/lib/sinon/util/core/extend.js b/lib/sinon/util/core/extend.js index d6b0f730d..2d3ad23cf 100644 --- a/lib/sinon/util/core/extend.js +++ b/lib/sinon/util/core/extend.js @@ -102,13 +102,24 @@ module.exports = function extend(target, ...sources) { if (prop === "name" && !destOwnPropertyDescriptor.writable) { return; } - - Object.defineProperty(dest, prop, { + const descriptors = { configurable: sourceOwnPropertyDescriptor.configurable, - enumerable: sourceOwnPropertyDescriptor.enumerable, - writable: sourceOwnPropertyDescriptor.writable, - value: sourceOwnPropertyDescriptor.value, - }); + enumerable: sourceOwnPropertyDescriptor.enumerable + } + /* + if the sorce has an Accessor property copy over the accessor functions (get and set) + data properties has writable attribute where as acessor property don't + REF: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#properties + */ + + if (hasOwnProperty(sourceOwnPropertyDescriptor, 'writable')) { + descriptors.writable = sourceOwnPropertyDescriptor.writable + descriptors.value = sourceOwnPropertyDescriptor.value + } else { + if (sourceOwnPropertyDescriptor.get) { descriptors.get = sourceOwnPropertyDescriptor.get.bind(dest) } + if (sourceOwnPropertyDescriptor.set) { descriptors.set = sourceOwnPropertyDescriptor.set.bind(dest) } + } + Object.defineProperty(dest, prop, descriptors); } ); }; diff --git a/test/extend-test.js b/test/extend-test.js index 17beb43e1..5f20f137a 100644 --- a/test/extend-test.js +++ b/test/extend-test.js @@ -78,6 +78,56 @@ describe("extend", function () { assert.equals(result, expected); }); + it("copies acessor properties into the target", function () { + var target = { + "hello" : "hello" + } + const obj = { + private: 1 + } + Object.defineProperty(obj, "lexical", { + configurable: true, + enumerable: true, + get: () => this.private, + set: value => { + this.private = value; + } + }); + Object.defineProperty(obj, "instance", { + configurable: true, + enumerable: true, + get: () => obj.private, + set: value => { + obj.private = value; + } + }); + Object.defineProperty(obj, "bound", { + configurable: true, + enumerable: true, + get: function () { return this.private }, + set: function (value) { + this.private = value; + } + }); + extend(target, obj); + assert.equals(target.hello, "hello") + assert.equals(target.lexical === undefined, true) + assert.equals(target.instance, 1) + assert.equals(target.bound, 1) + target.lexical = 2 + assert.equals(target.lexical, 2) + assert.equals(target.instance, 1) + assert.equals(target.bound, 1) + target.instance = 3 + assert.equals(target.lexical, 2) + assert.equals(target.instance, 3) + assert.equals(target.bound, 1) + target.bound = 4 + assert.equals(target.lexical, 2) + assert.equals(target.instance, 3) + assert.equals(target.bound, 4) + }) + context("when 'name' property is not writable", function () { it("does not attempt to write to the property", function () { var object1 = { prop1: null };