Skip to content

Commit 12a4593

Browse files
authored
Fix 2377: Throw error when trying to stub non-configurable or non-writable properties (#2417)
Fixes issue #2377 by throwing an error when trying to stub non-configurable or non-writable properties
1 parent 27df9cb commit 12a4593

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

lib/sinon/stub.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ function stub(object, property) {
8181
}
8282

8383
var actualDescriptor = getPropertyDescriptor(object, property);
84+
85+
assertValidPropertyDescriptor(actualDescriptor, property);
86+
8487
var isObjectOrFunction =
8588
typeof object === "object" || typeof object === "function";
8689
var isStubbingEntireObject =
@@ -147,6 +150,25 @@ stub.createStubInstance = function (constructor, overrides) {
147150
return stubbedObject;
148151
};
149152

153+
function assertValidPropertyDescriptor(descriptor, property) {
154+
if (!descriptor || !property) {
155+
return;
156+
}
157+
if (!descriptor.configurable && !descriptor.writable) {
158+
throw new TypeError(`Descriptor for property ${property} is non-configurable and non-writable`);
159+
}
160+
if ((descriptor.get || descriptor.set) && !descriptor.configurable) {
161+
throw new TypeError(`Descriptor for accessor property ${property} is non-configurable`);
162+
}
163+
if (isDataDescriptor(descriptor) && !descriptor.writable) {
164+
throw new TypeError(`Descriptor for data property ${property} is non-writable`);
165+
}
166+
}
167+
168+
function isDataDescriptor(descriptor) {
169+
return !descriptor.value && !descriptor.writable && !descriptor.set && !descriptor.get;
170+
}
171+
150172
/*eslint-disable no-use-before-define*/
151173
function getParentBehaviour(stubInstance) {
152174
return stubInstance.parent && getCurrentBehavior(stubInstance.parent);

test/shared-spy-stub-everything-tests.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,51 @@ module.exports = function shared(createSpyOrStub) {
151151

152152
assert.isUndefined(myObj.ouch);
153153
});
154+
155+
it("throws on data property descriptors that are not writable or configurable", function () {
156+
var myObj = {};
157+
Object.defineProperty(myObj, 'ignoreme', {
158+
writable: false,
159+
configurable: false
160+
});
161+
162+
assert.exception(function () {
163+
createSpyOrStub(myObj, "ignoreme");
164+
},
165+
new TypeError('Descriptor for property ignoreme is non-configurable and non-writable')
166+
);
167+
});
168+
169+
it("throws on accessor property descriptors that are not configurable", function () {
170+
var myObj = {};
171+
Object.defineProperty(myObj, 'ignoreme', {
172+
get: function(key) {
173+
return this[key];
174+
},
175+
set: function(key, val) {
176+
this[key] = val;
177+
},
178+
configurable: false
179+
});
180+
181+
assert.exception(function () {
182+
createSpyOrStub(myObj, "ignoreme");
183+
},
184+
new TypeError('Descriptor for accessor property ignoreme is non-configurable')
185+
);
186+
});
187+
188+
it("throws on data descriptors that are not stubbable", function () {
189+
var myObj = {};
190+
Object.defineProperty(myObj, 'ignoreme', {
191+
writable: false,
192+
configurable: false
193+
});
194+
195+
assert.exception(function () {
196+
createSpyOrStub(myObj, "ignoreme");
197+
},
198+
new TypeError('Descriptor for data property ignoreme is non-writable')
199+
);
200+
});
154201
};

0 commit comments

Comments
 (0)