diff --git a/lib/sinon/util/core/wrap-method.js b/lib/sinon/util/core/wrap-method.js index 8acc7eeb3..992b18d0d 100644 --- a/lib/sinon/util/core/wrap-method.js +++ b/lib/sinon/util/core/wrap-method.js @@ -137,7 +137,13 @@ module.exports = function wrapMethod(object, property, method) { for (i = 0; i < types.length; i++) { mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); } - methodDesc.configurable = true; + + // you are not allowed to flip the configurable prop on an + // existing descriptor to anything but false (#2514) + if (!owned) { + methodDesc.configurable = true; + } + Object.defineProperty(object, property, methodDesc); // catch failing assignment diff --git a/test/issues/issues-test.js b/test/issues/issues-test.js index e616e03a8..6a1b88b05 100644 --- a/test/issues/issues-test.js +++ b/test/issues/issues-test.js @@ -873,4 +873,30 @@ describe("issues", function () { }); }); }); + + describe("#2514 (regression from fixing #2491) - writable object descriptors that are unconfigurable should be assignable", function () { + function createInstanceWithWritableUconfigurablePropertyDescriptor() { + const instance = {}; + Object.defineProperty(instance, "aMethod", { + writable: true, + configurable: false, + value: function () { + return 42; + }, + }); + + return instance; + } + + it("should be able to assign and restore unconfigurable descriptors that are writable", function () { + const o = + createInstanceWithWritableUconfigurablePropertyDescriptor(); + + refute.exception(() => + this.sandbox.stub(o, "aMethod").returns("stubbed") + ); + assert.equals("stubbed", o.aMethod()); + this.sandbox.restore(); + }); + }); });