Implement spyMethod and use it to inspect the result of shouldComponentUpdate#1192
Implement spyMethod and use it to inspect the result of shouldComponentUpdate#1192ljharb merged 2 commits intoenzymejs:masterfrom
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.
Should we care about calling shouldComponentUpdate multiple times?
There was a problem hiding this comment.
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.
Maybe it should get the last call, not the first?
| } | ||
| instance.setState(state, callback); | ||
| if (spy) { | ||
| shouldRender = spy.getCall(0).returnValue; |
ljharb
left a comment
There was a problem hiding this comment.
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); |
packages/enzyme/src/Utils.js
Outdated
| restore() { | ||
| if (hasOwn) { | ||
| /* eslint-disable no-param-reassign */ | ||
| instance[methodName] = originalMethod; |
There was a problem hiding this comment.
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.
I don't think this needs to be, or should be, a getter - can it just be a function?
|
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.
this no longer seems to be checking disableLifecycleMethods?
There was a problem hiding this comment.
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.
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.
shouldn't this be !descriptor || !!descriptor.enumerable?
There was a problem hiding this comment.
But shouldn't an instance method be enumerable?
There was a problem hiding this comment.
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.
Should I check lifecycles.componentDidUpdate?
| // so we spy shouldComponentUpdate to get the result. | ||
| let shouldRender = true; | ||
| // dirty hack: | ||
| // make sure that componentWillReceiveProps is called before shouldComponentUpdate |
There was a problem hiding this comment.
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.
|
Thanks 🎉 |
This PR is to follow up #1133 (comment)