Skip to content

Commit 1e8c38e

Browse files
authored
Skip special nodes when reading TestInstance.parent (#12813)
1 parent fcff475 commit 1e8c38e

File tree

2 files changed

+51
-30
lines changed

2 files changed

+51
-30
lines changed

src/ReactTestRenderer.js

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,14 @@ class ReactTestInstance {
245245
}
246246

247247
get parent(): ?ReactTestInstance {
248-
const parent = this._fiber.return;
249-
return parent === null || parent.tag === HostRoot
250-
? null
251-
: wrapFiber(parent);
248+
let parent = this._fiber.return;
249+
while (parent !== null) {
250+
if (validWrapperTypes.has(parent.tag)) {
251+
return wrapFiber(parent);
252+
}
253+
parent = parent.return;
254+
}
255+
return null;
252256
}
253257

254258
get children(): Array<ReactTestInstance | string> {
@@ -262,30 +266,12 @@ class ReactTestInstance {
262266
node = node.child;
263267
outer: while (true) {
264268
let descend = false;
265-
switch (node.tag) {
266-
case FunctionalComponent:
267-
case ClassComponent:
268-
case HostComponent:
269-
case ForwardRef:
270-
children.push(wrapFiber(node));
271-
break;
272-
case HostText:
273-
children.push('' + node.memoizedProps);
274-
break;
275-
case Fragment:
276-
case ContextProvider:
277-
case ContextConsumer:
278-
case Mode:
279-
case Profiler:
280-
descend = true;
281-
break;
282-
default:
283-
invariant(
284-
false,
285-
'Unsupported component type %s in test renderer. ' +
286-
'This is probably a bug in React.',
287-
node.tag,
288-
);
269+
if (validWrapperTypes.has(node.tag)) {
270+
children.push(wrapFiber(node));
271+
} else if (node.tag === HostText) {
272+
children.push('' + node.memoizedProps);
273+
} else {
274+
descend = true;
289275
}
290276
if (descend && node.child !== null) {
291277
node.child.return = node;

src/__tests__/ReactTestRendererTraversal-test.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
const React = require('react');
1414
let ReactTestRenderer;
15+
let Context;
1516

1617
const RCTView = 'RCTView';
1718
const View = props => <RCTView {...props} />;
@@ -20,6 +21,7 @@ describe('ReactTestRendererTraversal', () => {
2021
beforeEach(() => {
2122
jest.resetModules();
2223
ReactTestRenderer = require('react-test-renderer');
24+
Context = React.createContext(null);
2325
});
2426

2527
class Example extends React.Component {
@@ -40,6 +42,17 @@ describe('ReactTestRendererTraversal', () => {
4042
<React.unstable_Profiler id="test" onRender={() => {}}>
4143
<ExampleForwardRef qux="qux" />
4244
</React.unstable_Profiler>
45+
<React.Fragment>
46+
<React.Fragment>
47+
<Context.Provider value={null}>
48+
<Context.Consumer>
49+
{() => <View nested={true} />}
50+
</Context.Consumer>
51+
</Context.Provider>
52+
</React.Fragment>
53+
<View nested={true} />
54+
<View nested={true} />
55+
</React.Fragment>
4356
</View>
4457
</View>
4558
);
@@ -61,7 +74,7 @@ describe('ReactTestRendererTraversal', () => {
6174

6275
// assert .props, .type and .parent attributes
6376
const foo = render.root.find(hasFooProp);
64-
expect(foo.props.children).toHaveLength(8);
77+
expect(foo.props.children).toHaveLength(9);
6578
expect(foo.type).toBe(View);
6679
expect(render.root.parent).toBe(null);
6780
expect(foo.children[0].parent).toBe(foo);
@@ -76,13 +89,15 @@ describe('ReactTestRendererTraversal', () => {
7689
const hasNullProp = node => node.props.hasOwnProperty('null');
7790
const hasVoidProp = node => node.props.hasOwnProperty('void');
7891
const hasItselfProp = node => node.props.hasOwnProperty('itself');
92+
const hasNestedProp = node => node.props.hasOwnProperty('nested');
7993

8094
expect(() => render.root.find(hasFooProp)).not.toThrow(); // 1 match
8195
expect(() => render.root.find(hasBarProp)).toThrow(); // >1 matches
8296
expect(() => render.root.find(hasBazProp)).toThrow(); // >1 matches
8397
expect(() => render.root.find(hasBingProp)).not.toThrow(); // 1 match
8498
expect(() => render.root.find(hasNullProp)).not.toThrow(); // 1 match
8599
expect(() => render.root.find(hasVoidProp)).toThrow(); // 0 matches
100+
expect(() => render.root.find(hasNestedProp)).toThrow(); // >1 matches
86101

87102
// same assertion as .find(), but confirm length
88103
expect(render.root.findAll(hasFooProp, {deep: false})).toHaveLength(1);
@@ -91,6 +106,7 @@ describe('ReactTestRendererTraversal', () => {
91106
expect(render.root.findAll(hasBingProp, {deep: false})).toHaveLength(1);
92107
expect(render.root.findAll(hasNullProp, {deep: false})).toHaveLength(1);
93108
expect(render.root.findAll(hasVoidProp, {deep: false})).toHaveLength(0);
109+
expect(render.root.findAll(hasNestedProp, {deep: false})).toHaveLength(3);
94110

95111
// note: with {deep: true}, .findAll() will continue to
96112
// search children, even after finding a match
@@ -100,6 +116,7 @@ describe('ReactTestRendererTraversal', () => {
100116
expect(render.root.findAll(hasBingProp)).toHaveLength(1); // no spread
101117
expect(render.root.findAll(hasNullProp)).toHaveLength(1); // no spread
102118
expect(render.root.findAll(hasVoidProp)).toHaveLength(0);
119+
expect(render.root.findAll(hasNestedProp, {deep: false})).toHaveLength(3);
103120

104121
const bing = render.root.find(hasBingProp);
105122
expect(bing.find(hasBarProp)).toBe(bing);
@@ -130,7 +147,7 @@ describe('ReactTestRendererTraversal', () => {
130147

131148
expect(render.root.findAllByType(ExampleFn)).toHaveLength(1);
132149
expect(render.root.findAllByType(View, {deep: false})).toHaveLength(1);
133-
expect(render.root.findAllByType(View)).toHaveLength(8);
150+
expect(render.root.findAllByType(View)).toHaveLength(11);
134151
expect(render.root.findAllByType(ExampleNull)).toHaveLength(2);
135152
expect(render.root.findAllByType(ExampleForwardRef)).toHaveLength(1);
136153

@@ -164,4 +181,22 @@ describe('ReactTestRendererTraversal', () => {
164181
expect(render.root.findAllByProps({baz})).toHaveLength(4);
165182
expect(render.root.findAllByProps({qux})).toHaveLength(3);
166183
});
184+
185+
it('skips special nodes', () => {
186+
const render = ReactTestRenderer.create(<Example />);
187+
expect(render.root.findAllByType(React.Fragment)).toHaveLength(0);
188+
expect(render.root.findAllByType(Context.Consumer)).toHaveLength(0);
189+
expect(render.root.findAllByType(Context.Provider)).toHaveLength(0);
190+
191+
const expectedParent = render.root.findByProps({foo: 'foo'}, {deep: false})
192+
.children[0];
193+
const nestedViews = render.root.findAllByProps(
194+
{nested: true},
195+
{deep: false},
196+
);
197+
expect(nestedViews.length).toBe(3);
198+
expect(nestedViews[0].parent).toBe(expectedParent);
199+
expect(nestedViews[1].parent).toBe(expectedParent);
200+
expect(nestedViews[2].parent).toBe(expectedParent);
201+
});
167202
});

0 commit comments

Comments
 (0)