Skip special nodes when reading TestInstance.parent (#12813)
Dan Abramov committed
May 15, 2018 at 11:11 UTC
7dc1a176b5c50739b200a9d8612a42b110289d22
2 files changed
+51
-30
packages/react-test-renderer/src/ReactTestRenderer.js
+14
-28
@@ -245,10 +245,14 @@ class ReactTestInstance {
245
}
246
247
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;
256
}
257
258
get children(): Array<ReactTestInstance | string> {
@@ -262,30 +266,12 @@ class ReactTestInstance {
266
node = node.child;
267
outer: while (true) {
268
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;
275
}
276
if (descend && node.child !== null) {
277
node.child.return = node;
packages/react-test-renderer/src/__tests__/ReactTestRendererTraversal-test.js
+37
-2
@@ -12,6 +12,7 @@
12
13
const React = require('react');
14
let ReactTestRenderer;
15
+let Context;
16
17
const RCTView = 'RCTView';
18
const View = props => <RCTView {...props} />;
@@ -20,6 +21,7 @@ describe('ReactTestRendererTraversal', () => {
21
beforeEach(() => {
22
jest.resetModules();
23
ReactTestRenderer = require('react-test-renderer');
24
+ Context = React.createContext(null);
25
});
26
27
class Example extends React.Component {
@@ -40,6 +42,17 @@ describe('ReactTestRendererTraversal', () => {
42
<React.unstable_Profiler id="test" onRender={() => {}}>
43
<ExampleForwardRef qux="qux" />
44
</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>
56
</View>
57
</View>
58
);
@@ -61,7 +74,7 @@ describe('ReactTestRendererTraversal', () => {
74
75
// assert .props, .type and .parent attributes
76
const foo = render.root.find(hasFooProp);
64
- expect(foo.props.children).toHaveLength(8);
77
+ expect(foo.props.children).toHaveLength(9);
78
expect(foo.type).toBe(View);
79
expect(render.root.parent).toBe(null);
80
expect(foo.children[0].parent).toBe(foo);
@@ -76,6 +89,7 @@ describe('ReactTestRendererTraversal', () => {
89
const hasNullProp = node => node.props.hasOwnProperty('null');
90
const hasVoidProp = node => node.props.hasOwnProperty('void');
91
const hasItselfProp = node => node.props.hasOwnProperty('itself');
92
+ const hasNestedProp = node => node.props.hasOwnProperty('nested');
93
94
expect(() => render.root.find(hasFooProp)).not.toThrow(); // 1 match
95
expect(() => render.root.find(hasBarProp)).toThrow(); // >1 matches
@@ -83,6 +97,7 @@ describe('ReactTestRendererTraversal', () => {
97
expect(() => render.root.find(hasBingProp)).not.toThrow(); // 1 match
98
expect(() => render.root.find(hasNullProp)).not.toThrow(); // 1 match
99
expect(() => render.root.find(hasVoidProp)).toThrow(); // 0 matches
100
+ expect(() => render.root.find(hasNestedProp)).toThrow(); // >1 matches
101
102
// same assertion as .find(), but confirm length
103
expect(render.root.findAll(hasFooProp, {deep: false})).toHaveLength(1);
@@ -91,6 +106,7 @@ describe('ReactTestRendererTraversal', () => {
106
expect(render.root.findAll(hasBingProp, {deep: false})).toHaveLength(1);
107
expect(render.root.findAll(hasNullProp, {deep: false})).toHaveLength(1);
108
expect(render.root.findAll(hasVoidProp, {deep: false})).toHaveLength(0);
109
+ expect(render.root.findAll(hasNestedProp, {deep: false})).toHaveLength(3);
110
111
// note: with {deep: true}, .findAll() will continue to
112
// search children, even after finding a match
@@ -100,6 +116,7 @@ describe('ReactTestRendererTraversal', () => {
116
expect(render.root.findAll(hasBingProp)).toHaveLength(1); // no spread
117
expect(render.root.findAll(hasNullProp)).toHaveLength(1); // no spread
118
expect(render.root.findAll(hasVoidProp)).toHaveLength(0);
119
+ expect(render.root.findAll(hasNestedProp, {deep: false})).toHaveLength(3);
120
121
const bing = render.root.find(hasBingProp);
122
expect(bing.find(hasBarProp)).toBe(bing);
@@ -130,7 +147,7 @@ describe('ReactTestRendererTraversal', () => {
147
148
expect(render.root.findAllByType(ExampleFn)).toHaveLength(1);
149
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);
151
expect(render.root.findAllByType(ExampleNull)).toHaveLength(2);
152
expect(render.root.findAllByType(ExampleForwardRef)).toHaveLength(1);
153
@@ -164,4 +181,22 @@ describe('ReactTestRendererTraversal', () => {
181
expect(render.root.findAllByProps({baz})).toHaveLength(4);
182
expect(render.root.findAllByProps({qux})).toHaveLength(3);
183
});
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
+ });
202
});