-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Implement spyMethod and use it to inspect the result of shouldComponentUpdate #1192
Conversation
…onentUpdate`/`getSnapshotBeforeUpdate`
if (props) this[UNRENDERED] = cloneElement(adapter, this[UNRENDERED], props); | ||
this[RENDERER].render(this[UNRENDERED], nextContext); | ||
if (spy) { | ||
shouldRender = spy.getCall(0).returnValue; |
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.
Should we care about calling shouldComponentUpdate
multiple times?
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.
Maybe it should get the last call, not the first?
if (props) this[UNRENDERED] = cloneElement(adapter, this[UNRENDERED], props); | ||
this[RENDERER].render(this[UNRENDERED], nextContext); | ||
if (spy) { | ||
shouldRender = spy.getCall(0).returnValue; |
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.
Maybe it should get the last call, not the first?
} | ||
instance.setState(state, callback); | ||
if (spy) { | ||
shouldRender = spy.getCall(0).returnValue; |
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.
Also here
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.
I've fixed it!
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.
Seems good to me!
Before we merge this, can we confirm what the oldest JS engine sinon supports is?
I'm not sure what the oldest JS engine sinon supports is. |
I'm not sure I agree with this change. This seems like a huge (and often problematic) dependency to take on for very little benefit. |
Fair point (not sure about problematic tho; sinon's always worked fine in my experience) the benefit I see is ensuring that the property descriptor remains the same after restoring, which is tedious to do correctly. |
Honestly I would prefer that we do our own thing here instead of pulling in sinon. If we need to do this in a more robust way and build a util function, or depend on a package that does just that, then that's fine. I'm also wondering if this would have strange conflicts with people using sinon on their own with |
@ljharb when i say "problematic" i mean that I know that in the past webpack and browserify both had problems bundling sinon. I think that might have been fixed in a more recent version though. |
Those are all valid concerns. Let's definitely hold off on merging this for now. |
I'm working on to implement own spyMethod instead of |
I've implemented a custom spy function, which is a minimum implementation needed by enzyme. |
packages/enzyme/src/Utils.js
Outdated
export function spyMethod(instance, methodName) { | ||
let lastReturnValue; | ||
const originalMethod = instance[methodName]; | ||
const hasOwn = Object.hasOwnProperty.call(instance, methodName); |
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.
this should use the has
package
packages/enzyme/src/Utils.js
Outdated
restore() { | ||
if (hasOwn) { | ||
/* eslint-disable no-param-reassign */ | ||
instance[methodName] = originalMethod; |
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.
this won't actually restore the original property descriptor; to do this properly, it needs to use Object.getOwnPropertyDescriptor
and Object.defineProperty
when available, and fall back to normal reading and assignment.
packages/enzyme/src/Utils.js
Outdated
/* eslint-enable no-param-reassign */ | ||
} | ||
}, | ||
get lastReturnValue() { |
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.
I don't think this needs to be, or should be, a getter - can it just be a function?
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.
Seems good, thanks
React has plans for v16.3. facebook/react#12152
Currently, ShallowWrapper overrides This PR will solve that. |
React v16.3 has been released. https://github.com/airbnb/enzyme/pull/1192/files#diff-fa27c82ee4fde075bae6173848e17c82L263 |
8795acb
to
50ddc88
Compare
312df6b
to
d5dc160
Compare
I'm not sure why CI is failing... |
All checks have passed 🎉 |
if ( | ||
!this[OPTIONS].disableLifecycleMethods && | ||
instance | ||
lifecycles.getSnapshotBeforeUpdate |
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.
this no longer seems to be checking disableLifecycleMethods
?
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.
Checking disableLifecycleMethods
is here.
https://github.com/airbnb/enzyme/pull/1192/files#diff-fa27c82ee4fde075bae6173848e17c82R338
lifecycles.getSnapshotBeforeUpdate | ||
&& typeof instance.getSnapshotBeforeUpdate === 'function' | ||
) { | ||
const snapshot = instance.getSnapshotBeforeUpdate(prevProps, state); |
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.
i have a mild preference for splitting the "snapshot" and "context" paths entirely, instead of trying to interleave them with let snapshot
packages/enzyme/src/Utils.js
Outdated
} | ||
Object.defineProperty(instance, methodName, { | ||
configurable: true, | ||
enumerable: false, |
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.
shouldn't this be !descriptor || !!descriptor.enumerable
?
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.
But shouldn't an instance method be enumerable?
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 replacement should be enumerable, or not, to match the one it's spying on
if (typeof instance.getSnapshotBeforeUpdate === 'function') { | ||
snapshot = instance.getSnapshotBeforeUpdate(prevProps, state); | ||
} | ||
if (typeof instance.componentDidUpdate === 'function') { |
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.
Should I check lifecycles.componentDidUpdate
?
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.
yes, that'd be great
let shouldRender = true; | ||
// dirty hack: | ||
// make sure that componentWillReceiveProps is called before shouldComponentUpdate |
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 no longer need this hack?
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.
Can you elaborate on why?
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 current version calls shouldComponentUpdate
manually to get the result.
But componentWillReceiveProps
should be called before shouldComponentUpdate
so we should call componentWillReceiveProps
manually before shouldComponentUpdate
.
In this PR, we no longer call shouldComponentUpdate
manually because we can spy the method.
As the result, we can remove the dirty hack because we don't need to call componentWillReceiveProps
and shouldComponentUpdate
manually.
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.
Perfect, thanks.
Thanks 🎉 |
This PR is to follow up #1133 (comment)