-
-
Notifications
You must be signed in to change notification settings - Fork 771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handling non-configurable object descriptors on the prototype #2508
Conversation
lib/sinon/util/core/wrap-method.js
Outdated
@@ -137,6 +137,7 @@ module.exports = function wrapMethod(object, property, method) { | |||
for (i = 0; i < types.length; i++) { | |||
mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); | |||
} | |||
methodDesc.configurable = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to store the original value, so it can be restored when the method is restored?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The restore()
function in this module already restores the original value. It only does so in the case of own descriptors, otherwise it just deletes the spy/stub (thus removing the shadowing of the prototype).
That's could help reduce the number of implementations 👍 |
It seems that
I agree, it should use |
Some background: Like you, I think that it would probably be best to leave |
EDIT: haha, turns out I brought the below up 3 years ago in #2195 🤣 So maybe create A side note A little detour into setting fakes
That means there is no easy way of cleaning up fakes set on instances of a class, right? Say we have this: class Foo{ method(){} }
const foo = new Foo(); We could easily clean up this with a sandbox.restore(): sandbox.stub(foo, 'method')
sandbox.restore() But setting and cleaning up using fakes looks like this: foo.method = sandbox.fake()
delete foo.method Should we not try to aim for consistency here? I think we have touched on this before (without anyone doing anything), but I could have liked for us to have this: sandbox.replace(foo, 'method', sandbox.fake(), {allowNonExisting: true})
sandbox.restore(); // like for everywhere else |
|
This shows an incoherent appraoch to how we deal with object descriptors across different code paths.
never supported undefined or protypal props in the first place. See sinonjs#2195 for backing discussion on creating sinon.define()
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #2508 +/- ##
=======================================
Coverage 95.99% 95.99%
=======================================
Files 40 40
Lines 1898 1899 +1
=======================================
+ Hits 1822 1823 +1
Misses 76 76
Flags with carried forward coverage won't be shown. Click here to find out more.
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
This change brought a decent amount of failures in our unit test coverage. We used sinon for stubbing various objects and ES imports. For example, a unit test where we stub some method in the Chaplin JS framework:
fails with
if I rollback this newly added one-liner
it works just fine. The ChaplinJS source code looks something like
|
@eugenet8k I am sorry to hear that, but I have no idea how this could happen from the brief description. Would it be possible for you to file a bug report with a reproducible example? Without code to reproduce an issue this will not be looked at. Guessing this happens in some proprietary product, so for comparison, this is what I usually do myself if I do not know how to recreate a minimal example from scratch:
Here's an example of what I run all production code through before it ends up on StackOverflow:
So I can do
to trim away all production references. |
@eugenet8k Fix in #2515. Please verify. |
Purpose (TL;DR) - mandatory
Fix for #2491 where it seems that we fail to restore spies set on instances if their prototypes have unconfigurable property descriptors.
Background (Problem in detail) - optional
A property descriptor must have
configurable: true
set for us to be able to restore it. If we get the property descriptor from a prototype we should be free to modify this to whatever we want.At first I hit issues when trying to achieve consistency for spies, stubs, mocks and fakes.
Fixing this in
wrap-method.js
only fixes the issue for spies as we deal with the issues in four different ways:stubs
Even though this module is also imported by the
stubs
module it fails for stubs before getting to thewrapMethod
call, as it has checks of their own on the object descriptors:Descriptor for property aMethod is non-configurable and non-writable
Mocks
Fixed by the above fix
fakes / sandbox.replace()
Fakes do not implement anything to do with replacing properties themselves and leave that to
Sandbox#replace
, which fails withCannot assign to read only property 'aMethod' of object '#<BaseClass>'
.The "weird" thing here is that we do not use any of the oldwrap-method
logic and instead have resorted to build new functionality for restoring. I am not sure why.EDIT: by intent. See discussion in #2195
Solution - optional
Setting
configurable: true
in wrap-method was only helpful for spies and mock.These are my thoughts for further work in this PR
The
Descriptor for property aMethod is non-configurable and non-writable
check is good and could be re-used elsewhere, but it needs to only apply to cases where the descriptor comes from the object, not its prototype. We can use theisOwn
property fromget-property-descriptor.js
to do this.Could the also perhaps be moved to
wrap-method
so that it would also be used for spies?Sandbox#replace
probably should not usewrap-method
, as it seems to have simplified the use-cases. Not sure thoughHow to verify - mandatory
npm i; npm test
Checklist for author
npm run lint
passesisOwn
andshadowsPropOnPrototype
is relevant