Let me preface this by saying I don't know how to reproduce the issue.
After upgrading to React 15, some of our tests were failing nondeterministically in our CI environment.
This was the component that was mounted. (component name and props edited for brevity)
import { mount } from 'enzyme';
import ComponentBeingTested from '..';
const component = mount(<ComponentBeingTested {...props} />);
console.log(component.debug());
// output is the same when test passed or failed
// <hoc1 {...props} >
// <hoc2 {...props} >
// <hoc3 {...props} >
// <button onClick={[Function]} style={{...}} disabled={true}>
// Test Child
// </button>
// </hoc3>
// </hoc2>
// </hoc1>
expect(component.find('button').prop('disabled')).toEqual(true);
// sometimes fails with: This method is only meant to be run on single node. 0 found instead.
I looked at how '.find' was implemented and wrote a method to traverse the tree in a similar manner to and print out what types were encountered.
https://gist.github.com/prayogoa/89f6b66fbb794613618d95a7214edb3a
calling this function with the component wrapper yields the following output when the spec succeeds:
import {traverse} from './traverse';
traverse(component);
/*
internalInstance(internalInstance(inst).getPublicInstance())._currentElement.type.name = '_class2'
internalInstance(internalInstance(inst)._renderedComponent.getPublicInstance())._currentElement.type.name = ''
internalInstance(internalInstance(inst)._reactInternalInstance._renderedComponent._renderedComponent.getPublicInstance())._currentElement.type.name = '_class'
internalInstance(internalInstance(inst)._renderedComponent._renderedComponent._renderedComponent.getPublicInstance())._currentElement.type = 'button'
*/
when the spec fails:
traverse(component);
/*
internalInstance(internalInstance(inst).getPublicInstance())._currentElement.type.name = '_class2'
internalInstance(internalInstance(inst)._renderedComponent.getPublicInstance())._currentElement.type.name = ''
internalInstance(internalInstance(inst)._renderedComponent._renderedComponent.getPublicInstance())._currentElement.type.name = '_class'
internalInstance(inst)._renderedComponent._renderedComponent._renderedComponent.getPublicInstance().type = 'submit'
*/
The last line is interesting, for some reason the type of button is perceived to be submit when the spec failed. It also traversed the tree differently.
I then dumped the component into a text file and diffed them.
component.flatMap(({ node }) => {
require('fs')
.appendFileSync(
`./component.json`,
require('util').inspect(node, { depth: 15 }),
{ encoding: 'utf8' }
);
});
component_fail.txt
component_pass.txt
I found this in the diff:
...
- _hostNode: HTMLButtonElement { '__reactInternalInstance$dfz94e4gxn8qia4i': [Circular] },
+ _hostNode: HTMLButtonElement { '__reactInternalInstance$bcytf4ap0isbgldi': undefined },
...
In the failing case, .__reactInternalInstance$... points to undefined, so the it looks at the type property of _hostNode, which is
document.createElement('button').type
-> "submit"
This seems like a bug, but with my limited knowledge of the inner workings of enzyme and react I don't know if it's a bug in enzyme (it should never look at the type property of a dom element), or in react itself (__reactInternalInstance&... should never be undefined in this case?). I hope by posting this here someone can guide me to the appropriate place to file this issue.
I believe I'm also being bit by this bug. Tests failing non-deterministically, but usually on a particular test suite in a repo of >6k tests. Thanks for the work you've done so far here, I'll try to take it further.
My tests are usually failing with error message:
Method “props” is only meant to be run on a single node. 0 found instead.
I forked enzyme to inject some debuggers into the ReactWrapper.single method which is the source of this error, and got the following results on a test failure:
const root = mount(...);
console.log('DEBUG: root', root);
component:
{ setChildProps:
{ [Function: bound setChildProps]
__reactBoundContext: [Circular],
__reactBoundMethod: [Function: setChildProps],
__reactBoundArguments: null,
bind: [Function] },
setChildContext:
{ [Function: bound setChildContext]
__reactBoundContext: [Circular],
__reactBoundMethod: [Function: setChildContext],
__reactBoundArguments: null,
bind: [Function] },
getInstance:
{ [Function: bound getInstance]
__reactBoundContext: [Circular],
__reactBoundMethod: [Function: getInstance],
__reactBoundArguments: null,
bind: [Function] },
getWrappedComponent:
{ [Function: bound getWrappedComponent]
__reactBoundContext: [Circular],
__reactBoundMethod: [Function: getWrappedComponent],
__reactBoundArguments: null,
bind: [Function] },
props: { Component: 'table', props: [Object], context: null },
context: {},
refs: {},
updater:
{ isMounted: [Function],
enqueueCallback: [Function],
enqueueCallbackInternal: [Function],
enqueueForceUpdate: [Function],
enqueueReplaceState: [Function],
enqueueSetState: [Function],
enqueueElementInternal: [Function],
validateCallback: [Function] },
state: { mount: true, props: [Object], context: null },
_reactInternalInstance:
{ _currentElement: [Object],
_rootNodeID: 0,
_compositeType: 0,
_instance: [Circular],
_hostParent: null,
_hostContainerInfo: [Object],
_updateBatchNumber: null,
_pendingElement: null,
_pendingStateQueue: null,
_pendingReplaceState: false,
_pendingForceUpdate: false,
_renderedNodeType: 0,
_renderedComponent: [Object],
_context: {},
_mountOrder: 15,
_topLevelWrapper: [Object],
_pendingCallbacks: null,
_calledComponentWillUnmount: false,
_warnedAboutRefsInRender: false,
_mountIndex: 0,
_mountImage: null,
_debugID: 25 } },
root: [Circular],
node: HTMLTableElement { '__reactInternalInstance$6jeh4la8lmsll3di': undefined },
nodes: [ HTMLTableElement { '__reactInternalInstance$6jeh4la8lmsll3di': undefined } ],
length: 1,
options: {},
complexSelector:
ComplexSelector {
buildPredicate: [Function: buildInstPredicate],
findWhereUnwrapped: [Function: findWhereUnwrapped],
childrenOfNode: [Function: childrenOfInst] } }
@prayogoa have you found any solution to this issue in the months since you've opened it?
@ryanwilsonperkin we have found that splitting our test suites into batches and restarting jest every batch made this issue go away. It's not ideal but we didn't have any more time to spend on this.
@prayogoa thanks! We've run into a similar issue where we've spent too much time trying (and failing) to properly replicate the issue. We've marked the 3 problematic tests as skipped for now, and hope to find a more permanent solution soon.
Hey! I believe we're also running into this issue, has there been any update on this?
@rwholey-eb I'm afraid not, after a fair bit of debugging I was unable to trace down the issue to a root cause.
Echoing what @rwholey-eb said, we've been running into this issue at Eventbrite trying everything we could think of to fix the issue. I was seeing the intermittent failures happening locally in our Docker environment too. And the failures happened more frequently as we added more tests. The problem definitely seemed to be with Enzyme.
And that proved to be the case because everything got fixed after upgrading to Enzyme 3! We followed the installation instructions for React 15 to add the appropriate adapter (we had to use enzyme-adapter-react-15.4 since we're still on 15.4.2). We did have to fix a few tests as a result of some breaking changes in Enzyme 3, but the migration guide explains what to look out for and what to fix.
Hope that helps!
@benmvp thanks for chiming in! We're running Enzyme 2.8.0 right now, but I'll put in a PR to update to 3 (we're also on React 15.4.2) and un-skip the tests we've had issues with. I'll report back if we run into any of the same errors again.
If anyone's reading this thread that knows what might have changed between 2.8 and 3.0 that might have fixed this issue, I'd love to know! This one's been one of those crazy bugs that are hard to pin down. I bet it's going to be something silly I wrote in the test 😅
@ryanwilsonperkin one thing is that we replaced the entire selector engine between enzyme 2 and 3.
@benmvp thanks for clarifying! You may also be interested in https://twitter.com/ljharb/status/931953598308679680 :-D
I think that based on the claim that it's fixed in v3; and because this bug is unlikely to get figured out or fixed in v2, I'm going to close this.
If anyone discovers issues in v3, or a different issue in v2, please file new issues - v2 backports are still possible, but I highly recommend upgrading to v3 ASAP.
Ah so I can use enzyme-adapter-react-helper instead of the specific enzyme-adapter-react-15.4? It'll figure it out for me? Nice! And thanks! 😄
Most helpful comment
Echoing what @rwholey-eb said, we've been running into this issue at Eventbrite trying everything we could think of to fix the issue. I was seeing the intermittent failures happening locally in our Docker environment too. And the failures happened more frequently as we added more tests. The problem definitely seemed to be with Enzyme.
And that proved to be the case because everything got fixed after upgrading to Enzyme 3! We followed the installation instructions for React 15 to add the appropriate adapter (we had to use
enzyme-adapter-react-15.4since we're still on 15.4.2). We did have to fix a few tests as a result of some breaking changes in Enzyme 3, but the migration guide explains what to look out for and what to fix.Hope that helps!