@samitouri / QOS-React / commits / 7884606fdb

Handle updates to natives and composites

Dan Abramov committed Apr 20, 2016 at 16:36 UTC 7884606fdb4519cc0a062eb7cd98cfc889cfd8b7
6 files changed +1593 -452
src/isomorphic/__tests__/ReactDebugTool-test.js
+1529 -422
@@ -12,36 +12,6 @@
12 'use strict';
13
14 describe('ReactDebugTool', () => {
15 -
16 -
17 -
18 -
19 -
20 -
21 -
22 -
23 -
24 -
25 -
26 -
27 - /*
28 - TODO:
29 - * Make sure update and unmount paths are tested
30 - * Make sure server rendering works the same way
31 - * Enable all existing tests
32 - * Remove ReactInstanceMap usage
33 - */
34 -
35 -
36 -
37 -
38 -
39 -
40 -
41 -
42 -
43 -
44 -
15 var React;
16 var ReactDebugTool;
17 var ReactDOM;
@@ -59,7 +29,25 @@ describe('ReactDebugTool', () => {
29 update(tree[debugID]);
30 }
31
62 - var devtool = {
32 + function getTree(debugID, includeOwner) {
33 + var item = tree[debugID];
34 + var result = {
35 + isComposite: item.isComposite,
36 + displayName: item.displayName,
37 + };
38 + result.children = item.childDebugIDs.map(childDebugID =>
39 + getTree(childDebugID, includeOwner)
40 + );
41 + if (item.text != null) {
42 + result.text = item.text;
43 + }
44 + if (includeOwner && item.ownerDebugID) {
45 + result.ownerDisplayName = tree[item.ownerDebugID].displayName;
46 + }
47 + return result;
48 + }
49 +
50 + return {
51 onSetIsComposite(debugID, isComposite) {
52 updateTree(debugID, item => item.isComposite = isComposite);
53 },
@@ -75,26 +63,10 @@ describe('ReactDebugTool', () => {
63 onSetText(debugID, text) {
64 updateTree(debugID, item => item.text = text);
65 },
78 - getTree(debugID, includeOwner) {
79 - var item = tree[debugID];
80 - var result = {
81 - isComposite: item.isComposite,
82 - displayName: item.displayName,
83 - };
84 - result.children = item.childDebugIDs.map(childDebugID =>
85 - devtool.getTree(childDebugID, includeOwner)
86 - );
87 - if (item.text != null) {
88 - result.text = item.text;
89 - }
90 - if (includeOwner && item.ownerDebugID) {
91 - result.ownerDisplayName = tree[item.ownerDebugID].displayName;
92 - }
93 - return result;
94 - }
66 + getTree(rootDebugID, includeOwner) {
67 + return getTree(rootDebugID, includeOwner);
68 + },
69 };
96 -
97 - return devtool;
70 }
71
72 beforeEach(() => {
@@ -113,446 +85,1581 @@ describe('ReactDebugTool', () => {
85 ReactDebugTool.removeDevtool(devtool);
86 });
87
116 - function assertTreeMatches(element, expectedTree, includeOwner) {
88 + function assertTreeMatches(pairs, includeOwner) {
89 + if (!Array.isArray(pairs[0])) {
90 + pairs = [pairs];
91 + }
92 +
93 + var currentElement;
94 class Wrapper extends React.Component {
95 render() {
119 - return element;
96 + return currentElement;
97 }
98 }
99
100 var node = document.createElement('div');
124 - var rootPublicInstance = ReactDOM.render(<Wrapper />, node);
125 - var rootInstance = ReactInstanceMap.get(rootPublicInstance);
126 - var actualTree = devtool.getTree(
127 - rootInstance._renderedComponent._debugID,
128 - includeOwner
129 - );
130 - expect(actualTree).toEqual(expectedTree);
131 - }
132 -
133 - it('uses displayName or Unknown for classic components', () => {
134 - var Foo = React.createClass({
135 - render() {
136 - return null;
137 - },
138 - });
139 - Foo.displayName = 'Bar';
140 - var Baz = React.createClass({
141 - render() {
142 - return null;
143 - },
144 - });
145 - var Qux = React.createClass({
146 - render() {
147 - return null;
148 - },
149 - });
150 - delete Qux.displayName;
151 -
152 - var element = <div><Foo /><Baz /><Qux /></div>;
153 - var expectedTree = {
154 - isComposite: false,
155 - displayName: 'div',
156 - children: [{
157 - isComposite: true,
158 - displayName: 'Bar',
159 - children: [],
160 - }, {
161 - isComposite: true,
162 - displayName: 'Baz',
163 - children: [],
164 - }, {
165 - isComposite: true,
166 - displayName: 'Unknown',
167 - children: [],
168 - }],
169 - };
170 - assertTreeMatches(element, expectedTree);
171 - });
101
173 - it('uses displayName, name, or ReactComponent for modern components', () => {
174 - class Foo extends React.Component {
175 - render() {
176 - return null;
177 - }
178 - }
179 - Foo.displayName = 'Bar';
180 - class Baz extends React.Component {
181 - render() {
182 - return null;
183 - }
184 - }
185 - class Qux extends React.Component {
186 - render() {
187 - return null;
188 - }
189 - }
190 - delete Qux.name;
102 + pairs.forEach(([element, expectedTree]) => {
103 + currentElement = element;
104
192 - var element = <div><Foo /><Baz /><Qux /></div>;
193 - var expectedTree = {
194 - isComposite: false,
195 - displayName: 'div',
196 - children: [{
197 - isComposite: true,
198 - displayName: 'Bar',
199 - children: [],
200 - }, {
201 - isComposite: true,
202 - displayName: 'Baz',
203 - children: [],
204 - }, {
205 - isComposite: true,
206 - // Note: Ideally fallback name should be consistent (e.g. "Unknown")
207 - displayName: 'ReactComponent',
208 - children: [],
209 - }],
210 - };
211 - assertTreeMatches(element, expectedTree);
212 - });
105 + var rootPublicInstance = ReactDOM.render(<Wrapper />, node);
106 + var rootInstance = ReactInstanceMap.get(rootPublicInstance);
107 + var actualTree = devtool.getTree(
108 + rootInstance._renderedComponent._debugID,
109 + includeOwner
110 + );
111 + expect(actualTree).toEqual(expectedTree);
112 + });
113 + }
114
214 - it('uses displayName, name, or Object for factory components', () => {
215 - function Foo() {
216 - return {
115 + describe('mount', () => {
116 + it('uses displayName or Unknown for classic components', () => {
117 + var Foo = React.createClass({
118 render() {
119 return null;
120 },
220 - };
221 - }
222 - Foo.displayName = 'Bar';
223 - function Baz() {
224 - return {
121 + });
122 + Foo.displayName = 'Bar';
123 + var Baz = React.createClass({
124 render() {
125 return null;
126 },
228 - };
229 - }
230 - function Qux() {
231 - return {
127 + });
128 + var Qux = React.createClass({
129 render() {
130 return null;
131 },
235 - };
236 - }
237 - delete Qux.name;
132 + });
133 + delete Qux.displayName;
134
239 - var element = <div><Foo /><Baz /><Qux /></div>;
240 - var expectedTree = {
241 - isComposite: false,
242 - displayName: 'div',
243 - children: [{
244 - isComposite: true,
245 - displayName: 'Bar',
246 - children: [],
247 - }, {
248 - isComposite: true,
249 - displayName: 'Baz',
250 - children: [],
251 - }, {
252 - isComposite: true,
253 - displayName: 'Unknown',
254 - children: [],
255 - }],
256 - };
257 - assertTreeMatches(element, expectedTree);
258 - });
259 -
260 - it('uses displayName, name, or StatelessComponent for functional components', () => {
261 - function Foo() {
262 - return null;
263 - }
264 - Foo.displayName = 'Bar';
265 - function Baz() {
266 - return null;
267 - }
268 - function Qux() {
269 - return null;
270 - }
271 - delete Qux.name;
135 + var element = <div><Foo /><Baz /><Qux /></div>;
136 + var tree = {
137 + isComposite: false,
138 + displayName: 'div',
139 + children: [{
140 + isComposite: true,
141 + displayName: 'Bar',
142 + children: [],
143 + }, {
144 + isComposite: true,
145 + displayName: 'Baz',
146 + children: [],
147 + }, {
148 + isComposite: true,
149 + displayName: 'Unknown',
150 + children: [],
151 + }],
152 + };
153 + assertTreeMatches([element, tree]);
154 + });
155
273 - var element = <div><Foo /><Baz /><Qux /></div>;
274 - var expectedTree = {
275 - isComposite: false,
276 - displayName: 'div',
277 - children: [{
278 - isComposite: true,
279 - displayName: 'Bar',
280 - children: [],
281 - }, {
282 - isComposite: true,
283 - displayName: 'Baz',
284 - children: [],
285 - }, {
286 - isComposite: true,
287 - displayName: 'Unknown',
288 - children: [],
289 - }],
290 - };
291 - assertTreeMatches(element, expectedTree);
292 - });
156 + it('uses displayName, name, or ReactComponent for modern components', () => {
157 + class Foo extends React.Component {
158 + render() {
159 + return null;
160 + }
161 + }
162 + Foo.displayName = 'Bar';
163 + class Baz extends React.Component {
164 + render() {
165 + return null;
166 + }
167 + }
168 + class Qux extends React.Component {
169 + render() {
170 + return null;
171 + }
172 + }
173 + delete Qux.name;
174
294 - it('reports a native tree correctly', () => {
295 - var element = (
296 - <div>
297 - <p>
298 - <span>
299 - Hi!
300 - </span>
301 - Wow.
302 - </p>
303 - <hr />
304 - </div>
305 - );
306 - var expectedTree = {
307 - isComposite: false,
308 - displayName: 'div',
309 - children: [{
175 + var element = <div><Foo /><Baz /><Qux /></div>;
176 + var tree = {
177 isComposite: false,
311 - displayName: 'p',
178 + displayName: 'div',
179 children: [{
313 - isComposite: false,
314 - displayName: 'span',
315 - children: [{
316 - isComposite: false,
317 - displayName: '#text',
318 - text: 'Hi!',
319 - children: [],
320 - }],
180 + isComposite: true,
181 + displayName: 'Bar',
182 + children: [],
183 }, {
322 - isComposite: false,
323 - displayName: '#text',
324 - text: 'Wow.',
184 + isComposite: true,
185 + displayName: 'Baz',
186 + children: [],
187 + }, {
188 + isComposite: true,
189 + // Note: Ideally fallback name should be consistent (e.g. "Unknown")
190 + displayName: 'ReactComponent',
191 children: [],
192 }],
327 - }, {
328 - isComposite: false,
329 - displayName: 'hr',
330 - children: [],
331 - }],
332 - };
333 - assertTreeMatches(element, expectedTree);
334 - });
193 + };
194 + assertTreeMatches([element, tree]);
195 + });
196
336 - it('reports a simple tree with composites correctly', () => {
337 - class Foo extends React.Component {
338 - render() {
339 - return <div />;
197 + it('uses displayName, name, or Object for factory components', () => {
198 + function Foo() {
199 + return {
200 + render() {
201 + return null;
202 + },
203 + };
204 }
341 - }
205 + Foo.displayName = 'Bar';
206 + function Baz() {
207 + return {
208 + render() {
209 + return null;
210 + },
211 + };
212 + }
213 + function Qux() {
214 + return {
215 + render() {
216 + return null;
217 + },
218 + };
219 + }
220 + delete Qux.name;
221
343 - var element = <Foo />;
344 - var expectedTree = {
345 - isComposite: true,
346 - displayName: 'Foo',
347 - children: [{
222 + var element = <div><Foo /><Baz /><Qux /></div>;
223 + var tree = {
224 isComposite: false,
225 displayName: 'div',
350 - children: [],
351 - }],
352 - };
353 - assertTreeMatches(element, expectedTree);
354 - });
226 + children: [{
227 + isComposite: true,
228 + displayName: 'Bar',
229 + children: [],
230 + }, {
231 + isComposite: true,
232 + displayName: 'Baz',
233 + children: [],
234 + }, {
235 + isComposite: true,
236 + displayName: 'Unknown',
237 + children: [],
238 + }],
239 + };
240 + assertTreeMatches([element, tree]);
241 + });
242
356 - it('reports a tree with composites correctly', () => {
357 - var Qux = React.createClass({
358 - render() {
243 + it('uses displayName, name, or StatelessComponent for functional components', () => {
244 + function Foo() {
245 return null;
360 - },
361 - });
362 - function Foo() {
363 - return {
364 - render() {
365 - return <Qux />;
366 - },
367 - };
368 - }
369 - function Bar({children}) {
370 - return <h1>{children}</h1>;
371 - }
372 - class Baz extends React.Component {
373 - render() {
374 - return (
375 - <div>
376 - <Foo />
377 - <Bar>
378 - <span>Hi,</span>
379 - Mom
380 - </Bar>
381 - <a href="#">Click me.</a>
382 - </div>
383 - );
246 }
385 - }
247 + Foo.displayName = 'Bar';
248 + function Baz() {
249 + return null;
250 + }
251 + function Qux() {
252 + return null;
253 + }
254 + delete Qux.name;
255
387 - var element = <Baz />;
388 - var expectedTree = {
389 - isComposite: true,
390 - displayName: 'Baz',
391 - children: [{
256 + var element = <div><Foo /><Baz /><Qux /></div>;
257 + var tree = {
258 isComposite: false,
259 displayName: 'div',
260 children: [{
261 isComposite: true,
396 - displayName: 'Foo',
397 - children: [{
398 - isComposite: true,
399 - displayName: 'Qux',
400 - children: [],
401 - }],
262 + displayName: 'Bar',
263 + children: [],
264 }, {
265 isComposite: true,
404 - displayName: 'Bar',
266 + displayName: 'Baz',
267 + children: [],
268 + }, {
269 + isComposite: true,
270 + displayName: 'Unknown',
271 + children: [],
272 + }],
273 + };
274 + assertTreeMatches([element, tree]);
275 + });
276 +
277 + it('reports a native tree correctly', () => {
278 + var element = (
279 + <div>
280 + <p>
281 + <span>
282 + Hi!
283 + </span>
284 + Wow.
285 + </p>
286 + <hr />
287 + </div>
288 + );
289 + var tree = {
290 + isComposite: false,
291 + displayName: 'div',
292 + children: [{
293 + isComposite: false,
294 + displayName: 'p',
295 children: [{
296 isComposite: false,
407 - displayName: 'h1',
297 + displayName: 'span',
298 children: [{
409 - isComposite: false,
410 - displayName: 'span',
411 - children: [{
412 - isComposite: false,
413 - displayName: '#text',
414 - text: 'Hi,',
415 - children: [],
416 - }],
417 - }, {
299 isComposite: false,
300 displayName: '#text',
420 - text: 'Mom',
301 + text: 'Hi!',
302 children: [],
303 }],
423 - }],
424 - }, {
425 - isComposite: false,
426 - displayName: 'a',
427 - children: [{
304 + }, {
305 isComposite: false,
306 displayName: '#text',
430 - text: 'Click me.',
307 + text: 'Wow.',
308 children: [],
309 }],
433 - }],
434 - }],
435 - };
436 - assertTreeMatches(element, expectedTree);
437 - });
310 + }, {
311 + isComposite: false,
312 + displayName: 'hr',
313 + children: [],
314 + }],
315 + };
316 + assertTreeMatches([element, tree]);
317 + });
318
439 - it('ignores null children', () => {
440 - class Foo extends React.Component {
441 - render() {
442 - return null;
319 + it('reports a simple tree with composites correctly', () => {
320 + class Foo extends React.Component {
321 + render() {
322 + return <div />;
323 + }
324 }
444 - }
445 - var element = <Foo />;
446 - var expectedTree = {
447 - isComposite: true,
448 - displayName: 'Foo',
449 - children: [],
450 - };
451 - assertTreeMatches(element, expectedTree);
452 - });
325
454 - it('ignores false children', () => {
455 - class Foo extends React.Component {
456 - render() {
457 - return false;
326 + var element = <Foo />;
327 + var tree = {
328 + isComposite: true,
329 + displayName: 'Foo',
330 + children: [{
331 + isComposite: false,
332 + displayName: 'div',
333 + children: [],
334 + }],
335 + };
336 + assertTreeMatches([element, tree]);
337 + });
338 +
339 + it('reports a tree with composites correctly', () => {
340 + var Qux = React.createClass({
341 + render() {
342 + return null;
343 + },
344 + });
345 + function Foo() {
346 + return {
347 + render() {
348 + return <Qux />;
349 + },
350 + };
351 + }
352 + function Bar({children}) {
353 + return <h1>{children}</h1>;
354 + }
355 + class Baz extends React.Component {
356 + render() {
357 + return (
358 + <div>
359 + <Foo />
360 + <Bar>
361 + <span>Hi,</span>
362 + Mom
363 + </Bar>
364 + <a href="#">Click me.</a>
365 + </div>
366 + );
367 + }
368 }
459 - }
460 - var element = <Foo />;
461 - var expectedTree = {
462 - isComposite: true,
463 - displayName: 'Foo',
464 - children: [],
465 - };
466 - assertTreeMatches(element, expectedTree);
467 - });
369
469 - it('reports text nodes as children', () => {
470 - var element = <div>{'1'}{2}</div>;
471 - var expectedTree = {
472 - isComposite: false,
473 - displayName: 'div',
474 - children: [{
475 - isComposite: false,
476 - displayName: '#text',
477 - text: '1',
370 + var element = <Baz />;
371 + var tree = {
372 + isComposite: true,
373 + displayName: 'Baz',
374 + children: [{
375 + isComposite: false,
376 + displayName: 'div',
377 + children: [{
378 + isComposite: true,
379 + displayName: 'Foo',
380 + children: [{
381 + isComposite: true,
382 + displayName: 'Qux',
383 + children: [],
384 + }],
385 + }, {
386 + isComposite: true,
387 + displayName: 'Bar',
388 + children: [{
389 + isComposite: false,
390 + displayName: 'h1',
391 + children: [{
392 + isComposite: false,
393 + displayName: 'span',
394 + children: [{
395 + isComposite: false,
396 + displayName: '#text',
397 + text: 'Hi,',
398 + children: [],
399 + }],
400 + }, {
401 + isComposite: false,
402 + displayName: '#text',
403 + text: 'Mom',
404 + children: [],
405 + }],
406 + }],
407 + }, {
408 + isComposite: false,
409 + displayName: 'a',
410 + children: [{
411 + isComposite: false,
412 + displayName: '#text',
413 + text: 'Click me.',
414 + children: [],
415 + }],
416 + }],
417 + }],
418 + };
419 + assertTreeMatches([element, tree]);
420 + });
421 +
422 + it('ignores null children', () => {
423 + class Foo extends React.Component {
424 + render() {
425 + return null;
426 + }
427 + }
428 + var element = <Foo />;
429 + var tree = {
430 + isComposite: true,
431 + displayName: 'Foo',
432 children: [],
479 - }, {
480 - isComposite: false,
481 - displayName: '#text',
482 - text: 2,
433 + };
434 + assertTreeMatches([element, tree]);
435 + });
436 +
437 + it('ignores false children', () => {
438 + class Foo extends React.Component {
439 + render() {
440 + return false;
441 + }
442 + }
443 + var element = <Foo />;
444 + var tree = {
445 + isComposite: true,
446 + displayName: 'Foo',
447 children: [],
484 - }],
485 - };
486 - assertTreeMatches(element, expectedTree);
487 - });
448 + };
449 + assertTreeMatches([element, tree]);
450 + });
451
489 - it('reports a single text node as a child', () => {
490 - var element = <div>{'1'}</div>;
491 - var expectedTree = {
492 - isComposite: false,
493 - displayName: 'div',
494 - children: [{
452 + it('reports text nodes as children', () => {
453 + var element = <div>{'1'}{2}</div>;
454 + var tree = {
455 isComposite: false,
496 - displayName: '#text',
497 - text: '1',
498 - children: [],
499 - }],
500 - };
501 - assertTreeMatches(element, expectedTree);
502 - });
456 + displayName: 'div',
457 + children: [{
458 + isComposite: false,
459 + displayName: '#text',
460 + text: '1',
461 + children: [],
462 + }, {
463 + isComposite: false,
464 + displayName: '#text',
465 + text: '2',
466 + children: [],
467 + }],
468 + };
469 + assertTreeMatches([element, tree]);
470 + });
471
504 - it('reports a single number node as a child', () => {
505 - var element = <div>{42}</div>;
506 - var expectedTree = {
507 - isComposite: false,
508 - displayName: 'div',
509 - children: [{
472 + it('reports a single text node as a child', () => {
473 + var element = <div>{'1'}</div>;
474 + var tree = {
475 isComposite: false,
511 - displayName: '#text',
512 - text: 42,
513 - children: [],
514 - }],
515 - };
516 - assertTreeMatches(element, expectedTree);
517 - });
476 + displayName: 'div',
477 + children: [{
478 + isComposite: false,
479 + displayName: '#text',
480 + text: '1',
481 + children: [],
482 + }],
483 + };
484 + assertTreeMatches([element, tree]);
485 + });
486
519 - it('skips empty nodes for multiple children', () => {
520 - function Foo() {
521 - return <div />;
522 - }
523 - var element = (
524 - <div>
525 - {'hi'}
526 - {false}
527 - {42}
528 - {null}
529 - <Foo />
530 - </div>
531 - );
532 - var expectedTree = {
533 - isComposite: false,
534 - displayName: 'div',
535 - children: [{
487 + it('reports a single number node as a child', () => {
488 + var element = <div>{42}</div>;
489 + var tree = {
490 isComposite: false,
537 - displayName: '#text',
538 - text: 'hi',
539 - children: [],
540 - }, {
491 + displayName: 'div',
492 + children: [{
493 + isComposite: false,
494 + displayName: '#text',
495 + text: '42',
496 + children: [],
497 + }],
498 + };
499 + assertTreeMatches([element, tree]);
500 + });
501 +
502 + it('reports a zero as a child', () => {
503 + var element = <div>{0}</div>;
504 + var tree = {
505 isComposite: false,
542 - displayName: '#text',
543 - text: 42,
544 - children: [],
545 - }, {
546 - isComposite: true,
547 - displayName: 'Foo',
506 + displayName: 'div',
507 children: [{
508 isComposite: false,
550 - displayName: 'div',
509 + displayName: '#text',
510 + text: '0',
511 children: [],
512 }],
553 - }],
554 - };
555 - assertTreeMatches(element, expectedTree);
513 + };
514 + assertTreeMatches([element, tree]);
515 + });
516 +
517 + it('skips empty nodes for multiple children', () => {
518 + function Foo() {
519 + return <div />;
520 + }
521 + var element = (
522 + <div>
523 + {'hi'}
524 + {false}
525 + {42}
526 + {null}
527 + <Foo />
528 + </div>
529 + );
530 + var tree = {
531 + isComposite: false,
532 + displayName: 'div',
533 + children: [{
534 + isComposite: false,
535 + displayName: '#text',
536 + text: 'hi',
537 + children: [],
538 + }, {
539 + isComposite: false,
540 + displayName: '#text',
541 + text: '42',
542 + children: [],
543 + }, {
544 + isComposite: true,
545 + displayName: 'Foo',
546 + children: [{
547 + isComposite: false,
548 + displayName: 'div',
549 + children: [],
550 + }],
551 + }],
552 + };
553 + assertTreeMatches([element, tree]);
554 + });
555 + });
556 +
557 + describe('update', () => {
558 + describe('native component', () => {
559 + it('updates text of a single text child', () => {
560 + var elementBefore = <div>Hi.</div>;
561 + var treeBefore = {
562 + isComposite: false,
563 + displayName: 'div',
564 + children: [{
565 + isComposite: false,
566 + displayName: '#text',
567 + text: 'Hi.',
568 + children: [],
569 + }],
570 + };
571 +
572 + var elementAfter = <div>Bye.</div>;
573 + var treeAfter = {
574 + isComposite: false,
575 + displayName: 'div',
576 + children: [{
577 + isComposite: false,
578 + displayName: '#text',
579 + text: 'Bye.',
580 + children: [],
581 + }],
582 + };
583 +
584 + assertTreeMatches([
585 + [elementBefore, treeBefore],
586 + [elementAfter, treeAfter],
587 + ]);
588 + });
589 +
590 + it('updates from no children to a single text child', () => {
591 + var elementBefore = <div />;
592 + var treeBefore = {
593 + isComposite: false,
594 + displayName: 'div',
595 + children: [],
596 + };
597 +
598 + var elementAfter = <div>Hi.</div>;
599 + var treeAfter = {
600 + isComposite: false,
601 + displayName: 'div',
602 + children: [{
603 + isComposite: false,
604 + displayName: '#text',
605 + text: 'Hi.',
606 + children: [],
607 + }],
608 + };
609 +
610 + assertTreeMatches([
611 + [elementBefore, treeBefore],
612 + [elementAfter, treeAfter],
613 + ]);
614 + });
615 +
616 + it('updates from a single text child to no children', () => {
617 + var elementBefore = <div>Hi.</div>;
618 + var treeBefore = {
619 + isComposite: false,
620 + displayName: 'div',
621 + children: [{
622 + isComposite: false,
623 + displayName: '#text',
624 + text: 'Hi.',
625 + children: [],
626 + }],
627 + };
628 +
629 + var elementAfter = <div />;
630 + var treeAfter = {
631 + isComposite: false,
632 + displayName: 'div',
633 + children: [],
634 + };
635 +
636 + assertTreeMatches([
637 + [elementBefore, treeBefore],
638 + [elementAfter, treeAfter],
639 + ]);
640 + });
641 +
642 + it('updates from no children to multiple text children', () => {
643 + var elementBefore = <div />;
644 + var treeBefore = {
645 + isComposite: false,
646 + displayName: 'div',
647 + children: [],
648 + };
649 +
650 + var elementAfter = <div>{'Hi.'}{'Bye.'}</div>;
651 + var treeAfter = {
652 + isComposite: false,
653 + displayName: 'div',
654 + children: [{
655 + isComposite: false,
656 + displayName: '#text',
657 + text: 'Hi.',
658 + children: [],
659 + }, {
660 + isComposite: false,
661 + displayName: '#text',
662 + text: 'Bye.',
663 + children: [],
664 + }],
665 + };
666 +
667 + assertTreeMatches([
668 + [elementBefore, treeBefore],
669 + [elementAfter, treeAfter],
670 + ]);
671 + });
672 +
673 + it('updates from multiple text children to no children', () => {
674 + var elementBefore = <div>{'Hi.'}{'Bye.'}</div>;
675 + var treeBefore = {
676 + isComposite: false,
677 + displayName: 'div',
678 + children: [{
679 + isComposite: false,
680 + displayName: '#text',
681 + text: 'Hi.',
682 + children: [],
683 + }, {
684 + isComposite: false,
685 + displayName: '#text',
686 + text: 'Bye.',
687 + children: [],
688 + }],
689 + };
690 +
691 + var elementAfter = <div />;
692 + var treeAfter = {
693 + isComposite: false,
694 + displayName: 'div',
695 + children: [],
696 + };
697 +
698 + assertTreeMatches([
699 + [elementBefore, treeBefore],
700 + [elementAfter, treeAfter],
701 + ]);
702 + });
703 +
704 + it('updates from one text child to multiple text children', () => {
705 + var elementBefore = <div>Hi.</div>;
706 + var treeBefore = {
707 + isComposite: false,
708 + displayName: 'div',
709 + children: [{
710 + isComposite: false,
711 + displayName: '#text',
712 + text: 'Hi.',
713 + children: [],
714 + }],
715 + };
716 +
717 + var elementAfter = <div>{'Hi.'}{'Bye.'}</div>;
718 + var treeAfter = {
719 + isComposite: false,
720 + displayName: 'div',
721 + children: [{
722 + isComposite: false,
723 + displayName: '#text',
724 + text: 'Hi.',
725 + children: [],
726 + }, {
727 + isComposite: false,
728 + displayName: '#text',
729 + text: 'Bye.',
730 + children: [],
731 + }],
732 + };
733 +
734 + assertTreeMatches([
735 + [elementBefore, treeBefore],
736 + [elementAfter, treeAfter],
737 + ]);
738 + });
739 +
740 + it('updates from multiple text children to one text child', () => {
741 + var elementBefore = <div>{'Hi.'}{'Bye.'}</div>;
742 + var treeBefore = {
743 + isComposite: false,
744 + displayName: 'div',
745 + children: [{
746 + isComposite: false,
747 + displayName: '#text',
748 + text: 'Hi.',
749 + children: [],
750 + }, {
751 + isComposite: false,
752 + displayName: '#text',
753 + text: 'Bye.',
754 + children: [],
755 + }],
756 + };
757 +
758 + var elementAfter = <div>Hi.</div>;
759 + var treeAfter = {
760 + isComposite: false,
761 + displayName: 'div',
762 + children: [{
763 + isComposite: false,
764 + displayName: '#text',
765 + text: 'Hi.',
766 + children: [],
767 + }],
768 + };
769 + assertTreeMatches([
770 + [elementBefore, treeBefore],
771 + [elementAfter, treeAfter],
772 + ]);
773 + });
774 +
775 + it('updates text nodes when reordering', () => {
776 + var elementBefore = <div>{'Hi.'}{'Bye.'}</div>;
777 + var treeBefore = {
778 + isComposite: false,
779 + displayName: 'div',
780 + children: [{
781 + isComposite: false,
782 + displayName: '#text',
783 + text: 'Hi.',
784 + children: [],
785 + }, {
786 + isComposite: false,
787 + displayName: '#text',
788 + text: 'Bye.',
789 + children: [],
790 + }],
791 + };
792 +
793 + var elementAfter = <div>{'Bye.'}{'Hi.'}</div>;
794 + var treeAfter = {
795 + isComposite: false,
796 + displayName: 'div',
797 + children: [{
798 + isComposite: false,
799 + displayName: '#text',
800 + text: 'Bye.',
801 + children: [],
802 + }, {
803 + isComposite: false,
804 + displayName: '#text',
805 + text: 'Hi.',
806 + children: [],
807 + }],
808 + };
809 + assertTreeMatches([
810 + [elementBefore, treeBefore],
811 + [elementAfter, treeAfter],
812 + ]);
813 + });
814 +
815 + it('updates native nodes when reordering with keys', () => {
816 + var elementBefore = (
817 + <div>
818 + <div key="a">Hi.</div>
819 + <div key="b">Bye.</div>
820 + </div>
821 + );
822 + var treeBefore = {
823 + isComposite: false,
824 + displayName: 'div',
825 + children: [{
826 + isComposite: false,
827 + displayName: 'div',
828 + children: [{
829 + isComposite: false,
830 + displayName: '#text',
831 + text: 'Hi.',
832 + children: [],
833 + }],
834 + }, {
835 + isComposite: false,
836 + displayName: 'div',
837 + children: [{
838 + isComposite: false,
839 + displayName: '#text',
840 + text: 'Bye.',
841 + children: [],
842 + }],
843 + }],
844 + };
845 +
846 + var elementAfter = (
847 + <div>
848 + <div key="b">Bye.</div>
849 + <div key="a">Hi.</div>
850 + </div>
851 + );
852 + var treeAfter = {
853 + isComposite: false,
854 + displayName: 'div',
855 + children: [{
856 + isComposite: false,
857 + displayName: 'div',
858 + children: [{
859 + isComposite: false,
860 + displayName: '#text',
861 + text: 'Bye.',
862 + children: [],
863 + }],
864 + }, {
865 + isComposite: false,
866 + displayName: 'div',
867 + children: [{
868 + isComposite: false,
869 + displayName: '#text',
870 + text: 'Hi.',
871 + children: [],
872 + }],
873 + }],
874 + };
875 +
876 + assertTreeMatches([
877 + [elementBefore, treeBefore],
878 + [elementAfter, treeAfter],
879 + ]);
880 + });
881 +
882 + it('updates native nodes when reordering without keys', () => {
883 + var elementBefore = (
884 + <div>
885 + <div>Hi.</div>
886 + <div>Bye.</div>
887 + </div>
888 + );
889 + var treeBefore = {
890 + isComposite: false,
891 + displayName: 'div',
892 + children: [{
893 + isComposite: false,
894 + displayName: 'div',
895 + children: [{
896 + isComposite: false,
897 + displayName: '#text',
898 + text: 'Hi.',
899 + children: [],
900 + }],
901 + }, {
902 + isComposite: false,
903 + displayName: 'div',
904 + children: [{
905 + isComposite: false,
906 + displayName: '#text',
907 + text: 'Bye.',
908 + children: [],
909 + }],
910 + }],
911 + };
912 +
913 + var elementAfter = (
914 + <div>
915 + <div>Bye.</div>
916 + <div>Hi.</div>
917 + </div>
918 + );
919 + var treeAfter = {
920 + isComposite: false,
921 + displayName: 'div',
922 + children: [{
923 + isComposite: false,
924 + displayName: 'div',
925 + children: [{
926 + isComposite: false,
927 + displayName: '#text',
928 + text: 'Bye.',
929 + children: [],
930 + }],
931 + }, {
932 + isComposite: false,
933 + displayName: 'div',
934 + children: [{
935 + isComposite: false,
936 + displayName: '#text',
937 + text: 'Hi.',
938 + children: [],
939 + }],
940 + }],
941 + };
942 +
943 + assertTreeMatches([
944 + [elementBefore, treeBefore],
945 + [elementAfter, treeAfter],
946 + ]);
947 + });
948 +
949 + it('updates a single composite child of a different type', () => {
950 + function Foo() {
951 + return null;
952 + }
953 +
954 + function Bar() {
955 + return null;
956 + }
957 +
958 + var elementBefore = <div><Foo /></div>;
959 + var treeBefore = {
960 + isComposite: false,
961 + displayName: 'div',
962 + children: [{
963 + isComposite: true,
964 + displayName: 'Foo',
965 + children: [],
966 + }],
967 + };
968 +
969 + var elementAfter = <div><Bar /></div>;
970 + var treeAfter = {
971 + isComposite: false,
972 + displayName: 'div',
973 + children: [{
974 + isComposite: true,
975 + displayName: 'Bar',
976 + children: [],
977 + }],
978 + };
979 +
980 + assertTreeMatches([
981 + [elementBefore, treeBefore],
982 + [elementAfter, treeAfter],
983 + ]);
984 + });
985 +
986 + it('updates a single composite child of the same type', () => {
987 + function Foo({ children }) {
988 + return children;
989 + }
990 +
991 + var elementBefore = <div><Foo><div /></Foo></div>;
992 + var treeBefore = {
993 + isComposite: false,
994 + displayName: 'div',
995 + children: [{
996 + isComposite: true,
997 + displayName: 'Foo',
998 + children: [{
999 + isComposite: false,
1000 + displayName: 'div',
1001 + children: [],
1002 + }],
1003 + }],
1004 + };
1005 +
1006 + var elementAfter = <div><Foo><span /></Foo></div>;
1007 + var treeAfter = {
1008 + isComposite: false,
1009 + displayName: 'div',
1010 + children: [{
1011 + isComposite: true,
1012 + displayName: 'Foo',
1013 + children: [{
1014 + isComposite: false,
1015 + displayName: 'span',
1016 + children: [],
1017 + }],
1018 + }],
1019 + };
1020 +
1021 + assertTreeMatches([
1022 + [elementBefore, treeBefore],
1023 + [elementAfter, treeAfter],
1024 + ]);
1025 + });
1026 +
1027 + it('updates from no children to a single composite child', () => {
1028 + function Foo() {
1029 + return null;
1030 + }
1031 +
1032 + var elementBefore = <div />;
1033 + var treeBefore = {
1034 + isComposite: false,
1035 + displayName: 'div',
1036 + children: [],
1037 + };
1038 +
1039 + var elementAfter = <div><Foo /></div>;
1040 + var treeAfter = {
1041 + isComposite: false,
1042 + displayName: 'div',
1043 + children: [{
1044 + isComposite: true,
1045 + displayName: 'Foo',
1046 + children: [],
1047 + }],
1048 + };
1049 +
1050 + assertTreeMatches([
1051 + [elementBefore, treeBefore],
1052 + [elementAfter, treeAfter],
1053 + ]);
1054 + });
1055 +
1056 + it('updates from a single composite child to no children', () => {
1057 + function Foo() {
1058 + return null;
1059 + }
1060 +
1061 + var elementBefore = <div><Foo /></div>;
1062 + var treeBefore = {
1063 + isComposite: false,
1064 + displayName: 'div',
1065 + children: [{
1066 + isComposite: true,
1067 + displayName: 'Foo',
1068 + children: [],
1069 + }],
1070 + };
1071 +
1072 + var elementAfter = <div />;
1073 + var treeAfter = {
1074 + isComposite: false,
1075 + displayName: 'div',
1076 + children: [],
1077 + };
1078 +
1079 + assertTreeMatches([
1080 + [elementBefore, treeBefore],
1081 + [elementAfter, treeAfter],
1082 + ]);
1083 + });
1084 +
1085 + it('updates mixed children', () => {
1086 + function Foo() {
1087 + return <div />;
1088 + }
1089 + var element1 = (
1090 + <div>
1091 + {'hi'}
1092 + {false}
1093 + {42}
1094 + {null}
1095 + <Foo />
1096 + </div>
1097 + );
1098 + var tree1 = {
1099 + isComposite: false,
1100 + displayName: 'div',
1101 + children: [{
1102 + isComposite: false,
1103 + displayName: '#text',
1104 + text: 'hi',
1105 + children: [],
1106 + }, {
1107 + isComposite: false,
1108 + displayName: '#text',
1109 + text: '42',
1110 + children: [],
1111 + }, {
1112 + isComposite: true,
1113 + displayName: 'Foo',
1114 + children: [{
1115 + isComposite: false,
1116 + displayName: 'div',
1117 + children: [],
1118 + }],
1119 + }],
1120 + };
1121 +
1122 + var element2 = (
1123 + <div>
1124 + <Foo />
1125 + {false}
1126 + {'hi'}
1127 + {null}
1128 + </div>
1129 + );
1130 + var tree2 = {
1131 + isComposite: false,
1132 + displayName: 'div',
1133 + children: [{
1134 + isComposite: true,
1135 + displayName: 'Foo',
1136 + children: [{
1137 + isComposite: false,
1138 + displayName: 'div',
1139 + children: [],
1140 + }],
1141 + }, {
1142 + isComposite: false,
1143 + displayName: '#text',
1144 + text: 'hi',
1145 + children: [],
1146 + }],
1147 + };
1148 +
1149 + var element3 = (
1150 + <div>
1151 + <Foo />
1152 + </div>
1153 + );
1154 + var tree3 = {
1155 + isComposite: false,
1156 + displayName: 'div',
1157 + children: [{
1158 + isComposite: true,
1159 + displayName: 'Foo',
1160 + children: [{
1161 + isComposite: false,
1162 + displayName: 'div',
1163 + children: [],
1164 + }],
1165 + }],
1166 + };
1167 +
1168 + assertTreeMatches([
1169 + [element1, tree1],
1170 + [element2, tree2],
1171 + [element3, tree3],
1172 + ]);
1173 + });
1174 + });
1175 +
1176 + describe('functional component', () => {
1177 + it('updates with a native child', () => {
1178 + function Foo({ children }) {
1179 + return children;
1180 + }
1181 +
1182 + var elementBefore = <Foo><div /></Foo>;
1183 + var treeBefore = {
1184 + isComposite: true,
1185 + displayName: 'Foo',
1186 + children: [{
1187 + isComposite: false,
1188 + displayName: 'div',
1189 + children: [],
1190 + }],
1191 + };
1192 +
1193 + var elementAfter = <Foo><span /></Foo>;
1194 + var treeAfter = {
1195 + isComposite: true,
1196 + displayName: 'Foo',
1197 + children: [{
1198 + isComposite: false,
1199 + displayName: 'span',
1200 + children: [],
1201 + }],
1202 + };
1203 +
1204 + assertTreeMatches([
1205 + [elementBefore, treeBefore],
1206 + [elementAfter, treeAfter],
1207 + ]);
1208 + });
1209 +
1210 + it('updates from null to a native child', () => {
1211 + function Foo({ children }) {
1212 + return children;
1213 + }
1214 +
1215 + var elementBefore = <Foo>{null}</Foo>;
1216 + var treeBefore = {
1217 + isComposite: true,
1218 + displayName: 'Foo',
1219 + children: [],
1220 + };
1221 +
1222 + var elementAfter = <Foo><div /></Foo>;
1223 + var treeAfter = {
1224 + isComposite: true,
1225 + displayName: 'Foo',
1226 + children: [{
1227 + isComposite: false,
1228 + displayName: 'div',
1229 + children: [],
1230 + }],
1231 + };
1232 +
1233 + assertTreeMatches([
1234 + [elementBefore, treeBefore],
1235 + [elementAfter, treeAfter],
1236 + ]);
1237 + });
1238 +
1239 + it('updates from a native child to null', () => {
1240 + function Foo({ children }) {
1241 + return children;
1242 + }
1243 +
1244 + var elementBefore = <Foo><div /></Foo>;
1245 + var treeBefore = {
1246 + isComposite: true,
1247 + displayName: 'Foo',
1248 + children: [{
1249 + isComposite: false,
1250 + displayName: 'div',
1251 + children: [],
1252 + }],
1253 + };
1254 +
1255 + var elementAfter = <Foo>{null}</Foo>;
1256 + var treeAfter = {
1257 + isComposite: true,
1258 + displayName: 'Foo',
1259 + children: [],
1260 + };
1261 +
1262 + assertTreeMatches([
1263 + [elementBefore, treeBefore],
1264 + [elementAfter, treeAfter],
1265 + ]);
1266 + });
1267 +
1268 + it('updates from a native child to a composite child', () => {
1269 + function Bar() {
1270 + return null;
1271 + }
1272 +
1273 + function Foo({ children }) {
1274 + return children;
1275 + }
1276 +
1277 + var elementBefore = <Foo><div /></Foo>;
1278 + var treeBefore = {
1279 + isComposite: true,
1280 + displayName: 'Foo',
1281 + children: [{
1282 + isComposite: false,
1283 + displayName: 'div',
1284 + children: [],
1285 + }],
1286 + };
1287 +
1288 + var elementAfter = <Foo><Bar /></Foo>;
1289 + var treeAfter = {
1290 + isComposite: true,
1291 + displayName: 'Foo',
1292 + children: [{
1293 + isComposite: true,
1294 + displayName: 'Bar',
1295 + children: [],
1296 + }],
1297 + };
1298 +
1299 + assertTreeMatches([
1300 + [elementBefore, treeBefore],
1301 + [elementAfter, treeAfter],
1302 + ]);
1303 + });
1304 +
1305 + it('updates from a composite child to a native child', () => {
1306 + function Bar() {
1307 + return null;
1308 + }
1309 +
1310 + function Foo({ children }) {
1311 + return children;
1312 + }
1313 +
1314 + var elementBefore = <Foo><Bar /></Foo>;
1315 + var treeBefore = {
1316 + isComposite: true,
1317 + displayName: 'Foo',
1318 + children: [{
1319 + isComposite: true,
1320 + displayName: 'Bar',
1321 + children: [],
1322 + }],
1323 + };
1324 +
1325 + var elementAfter = <Foo><div /></Foo>;
1326 + var treeAfter = {
1327 + isComposite: true,
1328 + displayName: 'Foo',
1329 + children: [{
1330 + isComposite: false,
1331 + displayName: 'div',
1332 + children: [],
1333 + }],
1334 + };
1335 +
1336 + assertTreeMatches([
1337 + [elementBefore, treeBefore],
1338 + [elementAfter, treeAfter],
1339 + ]);
1340 + });
1341 +
1342 + it('updates from null to a composite child', () => {
1343 + function Bar() {
1344 + return null;
1345 + }
1346 +
1347 + function Foo({ children }) {
1348 + return children;
1349 + }
1350 +
1351 + var elementBefore = <Foo>{null}</Foo>;
1352 + var treeBefore = {
1353 + isComposite: true,
1354 + displayName: 'Foo',
1355 + children: [],
1356 + };
1357 +
1358 + var elementAfter = <Foo><Bar /></Foo>;
1359 + var treeAfter = {
1360 + isComposite: true,
1361 + displayName: 'Foo',
1362 + children: [{
1363 + isComposite: true,
1364 + displayName: 'Bar',
1365 + children: [],
1366 + }],
1367 + };
1368 +
1369 + assertTreeMatches([
1370 + [elementBefore, treeBefore],
1371 + [elementAfter, treeAfter],
1372 + ]);
1373 + });
1374 +
1375 + it('updates from a composite child to null', () => {
1376 + function Bar() {
1377 + return null;
1378 + }
1379 +
1380 + function Foo({ children }) {
1381 + return children;
1382 + }
1383 +
1384 + var elementBefore = <Foo><Bar /></Foo>;
1385 + var treeBefore = {
1386 + isComposite: true,
1387 + displayName: 'Foo',
1388 + children: [{
1389 + isComposite: true,
1390 + displayName: 'Bar',
1391 + children: [],
1392 + }],
1393 + };
1394 +
1395 + var elementAfter = <Foo>{null}</Foo>;
1396 + var treeAfter = {
1397 + isComposite: true,
1398 + displayName: 'Foo',
1399 + children: [],
1400 + };
1401 +
1402 + assertTreeMatches([
1403 + [elementBefore, treeBefore],
1404 + [elementAfter, treeAfter],
1405 + ]);
1406 + });
1407 + });
1408 +
1409 + describe('class component', () => {
1410 + it('updates with a native child', () => {
1411 + var Foo = React.createClass({
1412 + render() {
1413 + return this.props.children;
1414 + },
1415 + });
1416 +
1417 + var elementBefore = <Foo><div /></Foo>;
1418 + var treeBefore = {
1419 + isComposite: true,
1420 + displayName: 'Foo',
1421 + children: [{
1422 + isComposite: false,
1423 + displayName: 'div',
1424 + children: [],
1425 + }],
1426 + };
1427 +
1428 + var elementAfter = <Foo><span /></Foo>;
1429 + var treeAfter = {
1430 + isComposite: true,
1431 + displayName: 'Foo',
1432 + children: [{
1433 + isComposite: false,
1434 + displayName: 'span',
1435 + children: [],
1436 + }],
1437 + };
1438 +
1439 + assertTreeMatches([
1440 + [elementBefore, treeBefore],
1441 + [elementAfter, treeAfter],
1442 + ]);
1443 + });
1444 +
1445 + it('updates from null to a native child', () => {
1446 + var Foo = React.createClass({
1447 + render() {
1448 + return this.props.children;
1449 + },
1450 + });
1451 +
1452 + var elementBefore = <Foo>{null}</Foo>;
1453 + var treeBefore = {
1454 + isComposite: true,
1455 + displayName: 'Foo',
1456 + children: [],
1457 + };
1458 +
1459 + var elementAfter = <Foo><div /></Foo>;
1460 + var treeAfter = {
1461 + isComposite: true,
1462 + displayName: 'Foo',
1463 + children: [{
1464 + isComposite: false,
1465 + displayName: 'div',
1466 + children: [],
1467 + }],
1468 + };
1469 +
1470 + assertTreeMatches([
1471 + [elementBefore, treeBefore],
1472 + [elementAfter, treeAfter],
1473 + ]);
1474 + });
1475 +
1476 + it('updates from a native child to null', () => {
1477 + var Foo = React.createClass({
1478 + render() {
1479 + return this.props.children;
1480 + },
1481 + });
1482 +
1483 + var elementBefore = <Foo><div /></Foo>;
1484 + var treeBefore = {
1485 + isComposite: true,
1486 + displayName: 'Foo',
1487 + children: [{
1488 + isComposite: false,
1489 + displayName: 'div',
1490 + children: [],
1491 + }],
1492 + };
1493 +
1494 + var elementAfter = <Foo>{null}</Foo>;
1495 + var treeAfter = {
1496 + isComposite: true,
1497 + displayName: 'Foo',
1498 + children: [],
1499 + };
1500 +
1501 + assertTreeMatches([
1502 + [elementBefore, treeBefore],
1503 + [elementAfter, treeAfter],
1504 + ]);
1505 + });
1506 +
1507 + it('updates from a native child to a composite child', () => {
1508 + var Bar = React.createClass({
1509 + render() {
1510 + return null;
1511 + },
1512 + });
1513 +
1514 + var Foo = React.createClass({
1515 + render() {
1516 + return this.props.children;
1517 + },
1518 + });
1519 +
1520 + var elementBefore = <Foo><div /></Foo>;
1521 + var treeBefore = {
1522 + isComposite: true,
1523 + displayName: 'Foo',
1524 + children: [{
1525 + isComposite: false,
1526 + displayName: 'div',
1527 + children: [],
1528 + }],
1529 + };
1530 +
1531 + var elementAfter = <Foo><Bar /></Foo>;
1532 + var treeAfter = {
1533 + isComposite: true,
1534 + displayName: 'Foo',
1535 + children: [{
1536 + isComposite: true,
1537 + displayName: 'Bar',
1538 + children: [],
1539 + }],
1540 + };
1541 +
1542 + assertTreeMatches([
1543 + [elementBefore, treeBefore],
1544 + [elementAfter, treeAfter],
1545 + ]);
1546 + });
1547 +
1548 + it('updates from a composite child to a native child', () => {
1549 + var Bar = React.createClass({
1550 + render() {
1551 + return null;
1552 + },
1553 + });
1554 +
1555 + var Foo = React.createClass({
1556 + render() {
1557 + return this.props.children;
1558 + },
1559 + });
1560 +
1561 + var elementBefore = <Foo><Bar /></Foo>;
1562 + var treeBefore = {
1563 + isComposite: true,
1564 + displayName: 'Foo',
1565 + children: [{
1566 + isComposite: true,
1567 + displayName: 'Bar',
1568 + children: [],
1569 + }],
1570 + };
1571 +
1572 + var elementAfter = <Foo><div /></Foo>;
1573 + var treeAfter = {
1574 + isComposite: true,
1575 + displayName: 'Foo',
1576 + children: [{
1577 + isComposite: false,
1578 + displayName: 'div',
1579 + children: [],
1580 + }],
1581 + };
1582 +
1583 + assertTreeMatches([
1584 + [elementBefore, treeBefore],
1585 + [elementAfter, treeAfter],
1586 + ]);
1587 + });
1588 +
1589 + it('updates from null to a composite child', () => {
1590 + var Bar = React.createClass({
1591 + render() {
1592 + return null;
1593 + },
1594 + });
1595 +
1596 + var Foo = React.createClass({
1597 + render() {
1598 + return this.props.children;
1599 + },
1600 + });
1601 +
1602 + var elementBefore = <Foo>{null}</Foo>;
1603 + var treeBefore = {
1604 + isComposite: true,
1605 + displayName: 'Foo',
1606 + children: [],
1607 + };
1608 +
1609 + var elementAfter = <Foo><Bar /></Foo>;
1610 + var treeAfter = {
1611 + isComposite: true,
1612 + displayName: 'Foo',
1613 + children: [{
1614 + isComposite: true,
1615 + displayName: 'Bar',
1616 + children: [],
1617 + }],
1618 + };
1619 +
1620 + assertTreeMatches([
1621 + [elementBefore, treeBefore],
1622 + [elementAfter, treeAfter],
1623 + ]);
1624 + });
1625 +
1626 + it('updates from a composite child to null', () => {
1627 + var Bar = React.createClass({
1628 + render() {
1629 + return null;
1630 + },
1631 + });
1632 +
1633 + var Foo = React.createClass({
1634 + render() {
1635 + return this.props.children;
1636 + },
1637 + });
1638 +
1639 + var elementBefore = <Foo><Bar /></Foo>;
1640 + var treeBefore = {
1641 + isComposite: true,
1642 + displayName: 'Foo',
1643 + children: [{
1644 + isComposite: true,
1645 + displayName: 'Bar',
1646 + children: [],
1647 + }],
1648 + };
1649 +
1650 + var elementAfter = <Foo>{null}</Foo>;
1651 + var treeAfter = {
1652 + isComposite: true,
1653 + displayName: 'Foo',
1654 + children: [],
1655 + };
1656 +
1657 + assertTreeMatches([
1658 + [elementBefore, treeBefore],
1659 + [elementAfter, treeAfter],
1660 + ]);
1661 + });
1662 + });
1663 });
1664
1665 it('tracks owner correctly', () => {
@@ -568,7 +1675,7 @@ describe('ReactDebugTool', () => {
1675 // Note that owner is not calculated for text nodes
1676 // because they are not created from real elements.
1677 var element = <article><Foo /></article>;
571 - var expectedTree = {
1678 + var tree = {
1679 isComposite: false,
1680 displayName: 'article',
1681 children: [{
@@ -602,6 +1709,6 @@ describe('ReactDebugTool', () => {
1709 }],
1710 }],
1711 };
605 - assertTreeMatches(element, expectedTree, true);
1712 + assertTreeMatches([element, tree], true);
1713 });
1714 });
src/renderers/dom/shared/ReactDOMComponent.js
+12 -1
@@ -763,7 +763,7 @@ ReactDOMComponent.Mixin = {
763 ReactInstrumentation.debugTool.onSetIsComposite(inlinedTextDebugID, false);
764 ReactInstrumentation.debugTool.onSetDisplayName(inlinedTextDebugID, '#text');
765 ReactInstrumentation.debugTool.onSetChildren(inlinedTextDebugID, []);
766 - ReactInstrumentation.debugTool.onSetText(inlinedTextDebugID, contentToUse);
766 + ReactInstrumentation.debugTool.onSetText(inlinedTextDebugID, '' + contentToUse);
767 }
768 DOMLazyTree.queueText(lazyTree, contentToUse);
769 } else if (childrenToUse != null) {
@@ -1012,11 +1012,22 @@ ReactDOMComponent.Mixin = {
1012 this.updateChildren(null, transaction, context);
1013 } else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
1014 this.updateTextContent('');
1015 + if (__DEV__) {
1016 + ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
1017 + }
1018 }
1019
1020 if (nextContent != null) {
1021 if (lastContent !== nextContent) {
1022 this.updateTextContent('' + nextContent);
1023 + if (__DEV__) {
1024 + var inlinedTextDebugID = this._debugID + '#text';
1025 + ReactInstrumentation.debugTool.onSetChildren(this._debugID, [inlinedTextDebugID]);
1026 + ReactInstrumentation.debugTool.onSetIsComposite(inlinedTextDebugID, false);
1027 + ReactInstrumentation.debugTool.onSetDisplayName(inlinedTextDebugID, '#text');
1028 + ReactInstrumentation.debugTool.onSetChildren(inlinedTextDebugID, []);
1029 + ReactInstrumentation.debugTool.onSetText(inlinedTextDebugID, '' + nextContent);
1030 + }
1031 }
1032 } else if (nextHtml != null) {
1033 if (lastHtml !== nextHtml) {
src/renderers/dom/shared/ReactDOMTextComponent.js
+7
@@ -14,6 +14,7 @@
14 var DOMChildrenOperations = require('DOMChildrenOperations');
15 var DOMLazyTree = require('DOMLazyTree');
16 var ReactDOMComponentTree = require('ReactDOMComponentTree');
17 +var ReactInstrumentation = require('ReactInstrumentation');
18 var ReactPerf = require('ReactPerf');
19
20 var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
@@ -67,6 +68,8 @@ Object.assign(ReactDOMTextComponent.prototype, {
68 context
69 ) {
70 if (__DEV__) {
71 + ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
72 +
73 var parentInfo;
74 if (nativeParent != null) {
75 parentInfo = nativeParent._ancestorInfo;
@@ -140,6 +143,10 @@ Object.assign(ReactDOMTextComponent.prototype, {
143 commentNodes[1],
144 nextStringText
145 );
146 +
147 + if (__DEV__) {
148 + ReactInstrumentation.debugTool.onSetText(this._debugID, nextStringText);
149 + }
150 }
151 }
152 },
src/renderers/shared/reconciler/ReactCompositeComponent.js
+14 -5
@@ -378,12 +378,12 @@ var ReactCompositeComponentMixin = {
378 renderedElement
379 );
380 if (__DEV__) {
381 - if (this._renderedComponent._debugID) {
382 - ReactInstrumentation.debugTool.onSetChildren(
383 - this._debugID,
381 + ReactInstrumentation.debugTool.onSetChildren(
382 + this._debugID,
383 + this._renderedNodeType === ReactNodeTypes.EMPTY ?
384 + [] :
385 [this._renderedComponent._debugID]
385 - );
386 - }
386 + );
387 }
388
389 var markup = ReactReconciler.mountComponent(
@@ -861,6 +861,15 @@ var ReactCompositeComponentMixin = {
861 this._renderedComponent = this._instantiateReactComponent(
862 nextRenderedElement
863 );
864 + if (__DEV__) {
865 + ReactInstrumentation.debugTool.onSetChildren(
866 + this._debugID,
867 + this._renderedNodeType === ReactNodeTypes.EMPTY ?
868 + [] :
869 + [this._renderedComponent._debugID]
870 + );
871 + }
872 +
873 var nextMarkup = ReactReconciler.mountComponent(
874 this._renderedComponent,
875 transaction,
src/renderers/shared/reconciler/ReactMultiChild.js
+19 -7
@@ -216,13 +216,6 @@ var ReactMultiChild = {
216 );
217 this._renderedChildren = children;
218
219 - if (__DEV__) {
220 - ReactInstrumentation.debugTool.onSetChildren(
221 - this._debugID,
222 - Object.keys(children).map(key => children[key]._debugID)
223 - );
224 - }
225 -
219 var mountImages = [];
220 var index = 0;
221 for (var name in children) {
@@ -239,6 +232,16 @@ var ReactMultiChild = {
232 mountImages.push(mountImage);
233 }
234 }
235 +
236 + if (__DEV__) {
237 + ReactInstrumentation.debugTool.onSetChildren(
238 + this._debugID,
239 + children ?
240 + Object.keys(children).map(key => children[key]._debugID) :
241 + []
242 + );
243 + }
244 +
245 return mountImages;
246 },
247
@@ -366,6 +369,15 @@ var ReactMultiChild = {
369 processQueue(this, updates);
370 }
371 this._renderedChildren = nextChildren;
372 +
373 + if (__DEV__) {
374 + ReactInstrumentation.debugTool.onSetChildren(
375 + this._debugID,
376 + nextChildren ?
377 + Object.keys(nextChildren).map(key => nextChildren[key]._debugID) :
378 + []
379 + );
380 + }
381 },
382
383 /**
src/renderers/shared/reconciler/instantiateReactComponent.js
+12 -17
@@ -42,18 +42,18 @@ function getDeclarationErrorAddendum(owner) {
42 }
43
44 function getDisplayName(instance) {
45 - var element = instance._currentElement;
46 - if (element == null) {
47 - return '#empty';
48 - } else if (typeof element === 'string' || typeof element === 'number') {
49 - return '#text';
50 - } else if (typeof element.type === 'string') {
51 - return element.type;
52 - } else if (instance.getName) {
53 - return instance.getName() || 'Unknown';
54 - } else {
55 - return element.type.displayName || element.type.name || 'Unknown';
56 - }
45 + var element = instance._currentElement;
46 + if (element == null) {
47 + return '#empty';
48 + } else if (typeof element === 'string' || typeof element === 'number') {
49 + return '#text';
50 + } else if (typeof element.type === 'string') {
51 + return element.type;
52 + } else if (instance.getName) {
53 + return instance.getName() || 'Unknown';
54 + } else {
55 + return element.type.displayName || element.type.name || 'Unknown';
56 + }
57 }
58
59 /**
@@ -85,7 +85,6 @@ function instantiateReactComponent(node) {
85 var instance;
86
87 var isEmpty = false;
88 - var isText = false;
88 var isComposite = false;
89
90 if (node === null || node === false) {
@@ -115,7 +114,6 @@ function instantiateReactComponent(node) {
114 instance = new ReactCompositeComponentWrapper(element);
115 }
116 } else if (typeof node === 'string' || typeof node === 'number') {
118 - isText = true;
117 instance = ReactNativeComponent.createInstanceForText(node);
118 } else {
119 invariant(
@@ -156,9 +154,6 @@ function instantiateReactComponent(node) {
154 if (owner) {
155 ReactInstrumentation.debugTool.onSetOwner(instance._debugID, owner._debugID);
156 }
159 - if (isText) {
160 - ReactInstrumentation.debugTool.onSetText(instance._debugID, node);
161 - }
157 }
158
159 // Internal instances should fully constructed at this point, so they should