@samitouri / QOS-React / commits / 3e2680411e

[Fiber] Fix reactComponentExpect (#8258)

* Add more reactComponentExpect tests * Implement reactComponentExpect in Fiber

Dan Abramov committed Nov 10, 2016 at 21:19 UTC 3e2680411efa3b478afc1f26057f417c7aae7915
5 files changed +245 -30
scripts/fiber/tests-failing.txt
-3
@@ -599,6 +599,3 @@ src/test/__tests__/ReactTestUtils-test.js
599 * should change the value of an input field in a component
600 * can scry with stateless components involved
601 * should set the type of the event
602 -
603 -src/test/__tests__/reactComponentExpect-test.js
604 -* should detect text components
scripts/fiber/tests-passing.txt
+7
@@ -1262,3 +1262,10 @@ src/test/__tests__/ReactTestUtils-test.js
1262 * can scryRenderedDOMComponentsWithClass with multiple classes
1263 * should throw when attempting to use ReactTestUtils.Simulate with shallow rendering
1264 * should not warn when simulating events with extra properties
1265 +
1266 +src/test/__tests__/reactComponentExpect-test.js
1267 +* should match composite components
1268 +* should match empty DOM components
1269 +* should match non-empty DOM components
1270 +* should match DOM component children
1271 +* should detect text components
src/test/ReactTestUtils.js
+3
@@ -165,6 +165,7 @@ var ReactTestUtils = {
165 return (constructor === type);
166 },
167
168 + // TODO: deprecate? It's undocumented and unused.
169 isCompositeComponentElement: function(inst) {
170 if (!React.isValidElement(inst)) {
171 return false;
@@ -178,6 +179,7 @@ var ReactTestUtils = {
179 );
180 },
181
182 + // TODO: deprecate? It's undocumented and unused.
183 isCompositeComponentElementWithType: function(inst, type) {
184 var internalInstance = ReactInstanceMap.get(inst);
185 var constructor = internalInstance
@@ -188,6 +190,7 @@ var ReactTestUtils = {
190 (constructor === type));
191 },
192
193 + // TODO: deprecate? It's undocumented and unused.
194 getRenderedChildOfCompositeComponent: function(inst) {
195 if (!ReactTestUtils.isCompositeComponent(inst)) {
196 return null;
src/test/__tests__/reactComponentExpect-test.js
+129 -1
@@ -23,6 +23,135 @@ describe('reactComponentExpect', () => {
23 reactComponentExpect = require('reactComponentExpect');
24 });
25
26 + it('should match composite components', () => {
27 + class SomeComponent extends React.Component {
28 + state = {y: 2};
29 + render() {
30 + return (
31 + <div className="Hello" />
32 + );
33 + }
34 + }
35 +
36 + var component = ReactTestUtils.renderIntoDocument(<SomeComponent x={1} />);
37 + reactComponentExpect(component)
38 + .toBePresent()
39 + .toBeCompositeComponent()
40 + .toBeComponentOfType(SomeComponent)
41 + .toBeCompositeComponentWithType(SomeComponent)
42 + .scalarPropsEqual({x: 1})
43 + .scalarStateEqual({y: 2});
44 + });
45 +
46 + it('should match empty DOM components', () => {
47 + class SomeComponent extends React.Component {
48 + render() {
49 + return (
50 + <div className="Hello" />
51 + );
52 + }
53 + }
54 +
55 + var component = ReactTestUtils.renderIntoDocument(<SomeComponent />);
56 + reactComponentExpect(component)
57 + .expectRenderedChild()
58 + .toBePresent()
59 + .toBeDOMComponent()
60 + .toBeDOMComponentWithNoChildren()
61 + .toBeComponentOfType('div');
62 + });
63 +
64 + it('should match non-empty DOM components', () => {
65 + class SomeComponent extends React.Component {
66 + render() {
67 + return (
68 + <div className="Hello">
69 + <p>1</p>
70 + <p>2</p>
71 + </div>
72 + );
73 + }
74 + }
75 +
76 + var component = ReactTestUtils.renderIntoDocument(<SomeComponent />);
77 + reactComponentExpect(component)
78 + .expectRenderedChild()
79 + .toBePresent()
80 + .toBeDOMComponent()
81 + .toBeDOMComponentWithChildCount(2)
82 + .toBeComponentOfType('div');
83 + });
84 +
85 + it('should match DOM component children', () => {
86 + class Inner extends React.Component {
87 + render() {
88 + return <section />;
89 + }
90 + }
91 +
92 + class Noop extends React.Component {
93 + render() {
94 + return null;
95 + }
96 + }
97 +
98 + class SomeComponent extends React.Component {
99 + render() {
100 + return (
101 + <div className="Hello">
102 + <p>1</p>
103 + <Inner foo="bar" />
104 + <span>{'Two'}{3}</span>
105 + <Noop />
106 + </div>
107 + );
108 + }
109 + }
110 +
111 + var component = ReactTestUtils.renderIntoDocument(<SomeComponent />);
112 + reactComponentExpect(component)
113 + .expectRenderedChild()
114 + .expectRenderedChildAt(0)
115 + .toBePresent()
116 + .toBeDOMComponent()
117 + .toBeDOMComponentWithNoChildren()
118 + .toBeComponentOfType('p');
119 +
120 + reactComponentExpect(component)
121 + .expectRenderedChild()
122 + .expectRenderedChildAt(1)
123 + .toBePresent()
124 + .toBeCompositeComponentWithType(Inner)
125 + .scalarPropsEqual({foo: 'bar'})
126 + .expectRenderedChild()
127 + .toBeComponentOfType('section')
128 + .toBeDOMComponentWithNoChildren();
129 +
130 + reactComponentExpect(component)
131 + .expectRenderedChild()
132 + .expectRenderedChildAt(2)
133 + .toBePresent()
134 + .toBeDOMComponent()
135 + .toBeComponentOfType('span')
136 + .toBeDOMComponentWithChildCount(2)
137 + .expectRenderedChildAt(0)
138 + .toBeTextComponentWithValue('Two');
139 +
140 + reactComponentExpect(component)
141 + .expectRenderedChild()
142 + .expectRenderedChildAt(2)
143 + .expectRenderedChildAt(1)
144 + .toBeTextComponentWithValue('3');
145 +
146 + reactComponentExpect(component)
147 + .expectRenderedChild()
148 + .expectRenderedChildAt(3)
149 + .toBePresent()
150 + .toBeCompositeComponentWithType(Noop)
151 + .expectRenderedChild()
152 + .toBeEmptyComponent();
153 + });
154 +
155 it('should detect text components', () => {
156 class SomeComponent extends React.Component {
157 render() {
@@ -36,7 +165,6 @@ describe('reactComponentExpect', () => {
165 }
166
167 var component = ReactTestUtils.renderIntoDocument(<SomeComponent />);
39 -
168 reactComponentExpect(component)
169 .expectRenderedChild()
170 .expectRenderedChildAt(1)
src/test/reactComponentExpect.js
+106 -26
@@ -13,9 +13,23 @@
13
14 var ReactInstanceMap = require('ReactInstanceMap');
15 var ReactTestUtils = require('ReactTestUtils');
16 +var ReactTypeOfWork = require('ReactTypeOfWork');
17 +
18 +var {
19 + HostText,
20 +} = ReactTypeOfWork;
21
22 var invariant = require('invariant');
23
24 +// Fiber doesn't actually have an instance for empty components
25 +// but we'll pretend it does while we keep compatibility with Stack.
26 +var fiberNullInstance = {
27 + type: null,
28 + child: null,
29 + sibling: null,
30 + tag: 99,
31 +};
32 +
33 function reactComponentExpect(instance) {
34 if (instance instanceof reactComponentExpectInternal) {
35 return instance;
@@ -52,7 +66,13 @@ Object.assign(reactComponentExpectInternal.prototype, {
66 * @instance: Retrieves the backing instance.
67 */
68 instance: function() {
55 - return this._instance.getPublicInstance();
69 + if (typeof this._instance.tag === 'number') {
70 + // Fiber reconciler
71 + return this._instance.stateNode;
72 + } else {
73 + // Stack reconciler
74 + return this._instance.getPublicInstance();
75 + }
76 },
77
78 /**
@@ -71,7 +91,14 @@ Object.assign(reactComponentExpectInternal.prototype, {
91 */
92 expectRenderedChild: function() {
93 this.toBeCompositeComponent();
74 - var child = this._instance._renderedComponent;
94 + var child = null;
95 + if (typeof this._instance.tag === 'number') {
96 + // Fiber reconciler
97 + child = this._instance.child || fiberNullInstance;
98 + } else {
99 + // Stack reconciler
100 + child = this._instance._renderedComponent;
101 + }
102 // TODO: Hide ReactEmptyComponent instances here?
103 return new reactComponentExpectInternal(child);
104 },
@@ -83,15 +110,30 @@ Object.assign(reactComponentExpectInternal.prototype, {
110 // Currently only dom components have arrays of children, but that will
111 // change soon.
112 this.toBeDOMComponent();
86 - var renderedChildren =
87 - this._instance._renderedChildren || {};
88 - for (var name in renderedChildren) {
89 - if (!renderedChildren.hasOwnProperty(name)) {
90 - continue;
113 +
114 + if (typeof this._instance.tag === 'number') {
115 + // Fiber reconciler
116 + var child = this._instance.child;
117 + var i = 0;
118 + while (child) {
119 + if (i === childIndex) {
120 + return new reactComponentExpectInternal(child);
121 + }
122 + child = child.sibling;
123 + i++;
124 }
92 - if (renderedChildren[name]) {
93 - if (renderedChildren[name]._mountIndex === childIndex) {
94 - return new reactComponentExpectInternal(renderedChildren[name]);
125 + } else {
126 + // Stack reconciler
127 + var renderedChildren =
128 + this._instance._renderedChildren || {};
129 + for (var name in renderedChildren) {
130 + if (!renderedChildren.hasOwnProperty(name)) {
131 + continue;
132 + }
133 + if (renderedChildren[name]) {
134 + if (renderedChildren[name]._mountIndex === childIndex) {
135 + return new reactComponentExpectInternal(renderedChildren[name]);
136 + }
137 }
138 }
139 }
@@ -100,24 +142,47 @@ Object.assign(reactComponentExpectInternal.prototype, {
142
143 toBeDOMComponentWithChildCount: function(count) {
144 this.toBeDOMComponent();
103 - var renderedChildren = this._instance._renderedChildren;
104 - expect(renderedChildren).toBeTruthy();
105 - expect(Object.keys(renderedChildren).length).toBe(count);
145 + if (typeof this._instance.tag === 'number') {
146 + // Fiber reconciler
147 + var child = this._instance.child;
148 + var i = 0;
149 + while (child) {
150 + child = child.sibling;
151 + i++;
152 + }
153 + expect(i).toBe(count);
154 + } else {
155 + // Stack reconciler
156 + var renderedChildren = this._instance._renderedChildren;
157 + if (count > 0) {
158 + expect(renderedChildren).toBeTruthy();
159 + expect(Object.keys(renderedChildren).length).toBe(count);
160 + } else {
161 + expect(renderedChildren).toBeFalsy();
162 + }
163 + }
164 return this;
165 },
166
167 toBeDOMComponentWithNoChildren: function() {
110 - this.toBeDOMComponent();
111 - expect(this._instance._renderedChildren).toBeFalsy();
168 + this.toBeDOMComponentWithChildCount(0);
169 return this;
170 },
171
172 // Matchers ------------------------------------------------------------------
173
174 toBeComponentOfType: function(constructor) {
118 - expect(
119 - this._instance._currentElement.type === constructor
120 - ).toBe(true);
175 + if (typeof this._instance.tag === 'number') {
176 + // Fiber reconciler
177 + expect(
178 + this._instance.type === constructor
179 + ).toBe(true);
180 + } else {
181 + // Stack reconciler
182 + expect(
183 + this._instance._currentElement.type === constructor
184 + ).toBe(true);
185 + }
186 return this;
187 },
188
@@ -126,6 +191,8 @@ Object.assign(reactComponentExpectInternal.prototype, {
191 * here.
192 */
193 toBeCompositeComponent: function() {
194 + // TODO: this code predates functional components
195 + // and doesn't work with them.
196 expect(
197 typeof this.instance() === 'object' &&
198 typeof this.instance().render === 'function'
@@ -135,22 +202,35 @@ Object.assign(reactComponentExpectInternal.prototype, {
202
203 toBeCompositeComponentWithType: function(constructor) {
204 this.toBeCompositeComponent();
138 - expect(
139 - this._instance._currentElement.type === constructor
140 - ).toBe(true);
205 + this.toBeComponentOfType(constructor);
206 return this;
207 },
208
209 toBeTextComponentWithValue: function(val) {
145 - var elementType = typeof this._instance._currentElement;
146 - expect(elementType === 'string' || elementType === 'number').toBe(true);
147 - expect(this._instance._stringText).toBe(val);
210 + if (typeof this._instance.tag === 'number') {
211 + // Fiber reconciler
212 + expect(this._instance.tag === HostText).toBe(true);
213 + var actualVal = this._instance.memoizedProps;
214 + expect(typeof actualVal === 'string' || typeof actualVal === 'number').toBe(true);
215 + expect(String(actualVal)).toBe(val);
216 + } else {
217 + // Fiber reconciler
218 + var elementType = typeof this._instance._currentElement;
219 + expect(elementType === 'string' || elementType === 'number').toBe(true);
220 + expect(this._instance._stringText).toBe(val);
221 + }
222 return this;
223 },
224
225 toBeEmptyComponent: function() {
152 - var element = this._instance._currentElement;
153 - return element === null || element === false;
226 + if (typeof this._instance.tag === 'number') {
227 + // Fiber reconciler
228 + expect(this._instance).toBe(fiberNullInstance);
229 + } else {
230 + // Stack reconciler
231 + var element = this._instance._currentElement;
232 + expect(element === null || element === false).toBe(true);
233 + }
234 },
235
236 toBePresent: function() {