/** * Copyright 2016-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @emails react-core */ 'use strict'; describe('ReactComponentTreeDevtool', () => { var React; var ReactDebugTool; var ReactDOM; var ReactDOMServer; var ReactInstanceMap; var ReactComponentTreeDevtool; beforeEach(() => { jest.resetModuleRegistry(); React = require('React'); ReactDebugTool = require('ReactDebugTool'); ReactDOM = require('ReactDOM'); ReactDOMServer = require('ReactDOMServer'); ReactInstanceMap = require('ReactInstanceMap'); ReactComponentTreeDevtool = require('ReactComponentTreeDevtool'); ReactDebugTool.addDevtool(ReactComponentTreeDevtool); }); afterEach(() => { ReactDebugTool.removeDevtool(ReactComponentTreeDevtool); }); function denormalizeTree(tree, rootID, includeOwner) { var item = tree[rootID]; var result = { isComposite: item.isComposite, displayName: item.displayName, }; if (item.childIDs) { result.children = item.childIDs.map(childID => denormalizeTree(tree, childID, includeOwner) ); } if (item.text != null) { result.text = item.text; } if (includeOwner && item.ownerDebugID) { result.ownerDisplayName = tree[item.ownerDebugID].displayName; } return result; } function getRegisteredDisplayNames(tree) { return Object.keys(tree).map(id => tree[id].displayName); } function assertTreeMatches(pairs, includeOwner) { if (!Array.isArray(pairs[0])) { pairs = [pairs]; } var node = document.createElement('div'); var currentElement; var rootInstance; class Wrapper extends React.Component { render() { rootInstance = ReactInstanceMap.get(this); return currentElement; } } pairs.forEach(([element, expectedTree]) => { currentElement = element; ReactDOM.render(, node); expect(denormalizeTree( ReactComponentTreeDevtool.getTree(), rootInstance._renderedComponent._debugID, includeOwner )).toEqual(expectedTree); }); ReactDOM.unmountComponentAtNode(node); expect( getRegisteredDisplayNames(ReactComponentTreeDevtool.getTree()) ).toEqual([]); pairs.forEach(([element, expectedTree]) => { currentElement = element; ReactDOMServer.renderToString(); expect(denormalizeTree( ReactComponentTreeDevtool.getTree(), rootInstance._renderedComponent._debugID, includeOwner )).toEqual(expectedTree); ReactComponentTreeDevtool.purgeUnmountedContainers(); expect( getRegisteredDisplayNames(ReactComponentTreeDevtool.getTree()) ).toEqual([]); }); } describe('mount', () => { it('uses displayName or Unknown for classic components', () => { var Foo = React.createClass({ render() { return null; }, }); Foo.displayName = 'Bar'; var Baz = React.createClass({ render() { return null; }, }); var Qux = React.createClass({ render() { return null; }, }); delete Qux.displayName; var element =
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Bar', children: [], }, { isComposite: true, displayName: 'Baz', children: [], }, { isComposite: true, displayName: 'Unknown', children: [], }], }; assertTreeMatches([element, tree]); }); it('uses displayName, name, or ReactComponent for modern components', () => { class Foo extends React.Component { render() { return null; } } Foo.displayName = 'Bar'; class Baz extends React.Component { render() { return null; } } class Qux extends React.Component { render() { return null; } } delete Qux.name; var element =
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Bar', children: [], }, { isComposite: true, displayName: 'Baz', children: [], }, { isComposite: true, // Note: Ideally fallback name should be consistent (e.g. "Unknown") displayName: 'ReactComponent', children: [], }], }; assertTreeMatches([element, tree]); }); it('uses displayName, name, or Object for factory components', () => { function Foo() { return { render() { return null; }, }; } Foo.displayName = 'Bar'; function Baz() { return { render() { return null; }, }; } function Qux() { return { render() { return null; }, }; } delete Qux.name; var element =
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Bar', children: [], }, { isComposite: true, displayName: 'Baz', children: [], }, { isComposite: true, displayName: 'Unknown', children: [], }], }; assertTreeMatches([element, tree]); }); it('uses displayName, name, or StatelessComponent for functional components', () => { function Foo() { return null; } Foo.displayName = 'Bar'; function Baz() { return null; } function Qux() { return null; } delete Qux.name; var element =
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Bar', children: [], }, { isComposite: true, displayName: 'Baz', children: [], }, { isComposite: true, displayName: 'Unknown', children: [], }], }; assertTreeMatches([element, tree]); }); it('reports a native tree correctly', () => { var element = (

Hi! Wow.


); var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: 'p', children: [{ isComposite: false, displayName: 'span', children: [{ isComposite: false, displayName: '#text', text: 'Hi!', }], }, { isComposite: false, displayName: '#text', text: 'Wow.', }], }, { isComposite: false, displayName: 'hr', children: [], }], }; assertTreeMatches([element, tree]); }); it('reports a simple tree with composites correctly', () => { class Foo extends React.Component { render() { return
; } } var element = ; var tree = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; assertTreeMatches([element, tree]); }); it('reports a tree with composites correctly', () => { var Qux = React.createClass({ render() { return null; }, }); function Foo() { return { render() { return ; }, }; } function Bar({children}) { return

{children}

; } class Baz extends React.Component { render() { return (
Hi, Mom Click me.
); } } var element = ; var tree = { isComposite: true, displayName: 'Baz', children: [{ isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Qux', children: [], }], }, { isComposite: true, displayName: 'Bar', children: [{ isComposite: false, displayName: 'h1', children: [{ isComposite: false, displayName: 'span', children: [{ isComposite: false, displayName: '#text', text: 'Hi,', }], }, { isComposite: false, displayName: '#text', text: 'Mom', }], }], }, { isComposite: false, displayName: 'a', children: [{ isComposite: false, displayName: '#text', text: 'Click me.', }], }], }], }; assertTreeMatches([element, tree]); }); it('ignores null children', () => { class Foo extends React.Component { render() { return null; } } var element = ; var tree = { isComposite: true, displayName: 'Foo', children: [], }; assertTreeMatches([element, tree]); }); it('ignores false children', () => { class Foo extends React.Component { render() { return false; } } var element = ; var tree = { isComposite: true, displayName: 'Foo', children: [], }; assertTreeMatches([element, tree]); }); it('reports text nodes as children', () => { var element =
{'1'}{2}
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: '1', }, { isComposite: false, displayName: '#text', text: '2', }], }; assertTreeMatches([element, tree]); }); it('reports a single text node as a child', () => { var element =
{'1'}
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: '1', }], }; assertTreeMatches([element, tree]); }); it('reports a single number node as a child', () => { var element =
{42}
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: '42', }], }; assertTreeMatches([element, tree]); }); it('reports a zero as a child', () => { var element =
{0}
; var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: '0', }], }; assertTreeMatches([element, tree]); }); it('skips empty nodes for multiple children', () => { function Foo() { return
; } var element = (
{'hi'} {false} {42} {null}
); var tree = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'hi', }, { isComposite: false, displayName: '#text', text: '42', }, { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }], }; assertTreeMatches([element, tree]); }); it('reports html content as no children', () => { var element =
; var tree = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([element, tree]); }); }); describe('update', () => { describe('native component', () => { it('updates text of a single text child', () => { var elementBefore =
Hi.
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }; var elementAfter =
Bye.
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Bye.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from no children to a single text child', () => { var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [], }; var elementAfter =
Hi.
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a single text child to no children', () => { var elementBefore =
Hi.
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from html content to a single text child', () => { var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [], }; var elementAfter =
Hi.
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a single text child to html content', () => { var elementBefore =
Hi.
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from no children to multiple text children', () => { var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [], }; var elementAfter =
{'Hi.'}{'Bye.'}
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }, { isComposite: false, displayName: '#text', text: 'Bye.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from multiple text children to no children', () => { var elementBefore =
{'Hi.'}{'Bye.'}
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }, { isComposite: false, displayName: '#text', text: 'Bye.', }], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from html content to multiple text children', () => { var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [], }; var elementAfter =
{'Hi.'}{'Bye.'}
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }, { isComposite: false, displayName: '#text', text: 'Bye.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from multiple text children to html content', () => { var elementBefore =
{'Hi.'}{'Bye.'}
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }, { isComposite: false, displayName: '#text', text: 'Bye.', }], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from html content to no children', () => { var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from no children to html content', () => { var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from one text child to multiple text children', () => { var elementBefore =
Hi.
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }; var elementAfter =
{'Hi.'}{'Bye.'}
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }, { isComposite: false, displayName: '#text', text: 'Bye.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from multiple text children to one text child', () => { var elementBefore =
{'Hi.'}{'Bye.'}
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }, { isComposite: false, displayName: '#text', text: 'Bye.', }], }; var elementAfter =
Hi.
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates text nodes when reordering', () => { var elementBefore =
{'Hi.'}{'Bye.'}
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }, { isComposite: false, displayName: '#text', text: 'Bye.', }], }; var elementAfter =
{'Bye.'}{'Hi.'}
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Bye.', }, { isComposite: false, displayName: '#text', text: 'Hi.', }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates native nodes when reordering with keys', () => { var elementBefore = (
Hi.
Bye.
); var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }, { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Bye.', }], }], }; var elementAfter = (
Bye.
Hi.
); var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Bye.', }], }, { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates native nodes when reordering without keys', () => { var elementBefore = (
Hi.
Bye.
); var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }, { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Bye.', }], }], }; var elementAfter = (
Bye.
Hi.
); var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Bye.', }], }, { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates a single composite child of a different type', () => { function Foo() { return null; } function Bar() { return null; } var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [], }], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates a single composite child of the same type', () => { function Foo({ children }) { return children; } var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'span', children: [], }], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from no children to a single composite child', () => { function Foo() { return null; } var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a single composite child to no children', () => { function Foo() { return null; } var elementBefore =
; var treeBefore = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [], }], }; var elementAfter =
; var treeAfter = { isComposite: false, displayName: 'div', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates mixed children', () => { function Foo() { return
; } var element1 = (
{'hi'} {false} {42} {null}
); var tree1 = { isComposite: false, displayName: 'div', children: [{ isComposite: false, displayName: '#text', text: 'hi', }, { isComposite: false, displayName: '#text', text: '42', }, { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }], }; var element2 = (
{false} {'hi'} {null}
); var tree2 = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }, { isComposite: false, displayName: '#text', text: 'hi', }], }; var element3 = (
); var tree3 = { isComposite: false, displayName: 'div', children: [{ isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }], }; assertTreeMatches([ [element1, tree1], [element2, tree2], [element3, tree3], ]); }); }); describe('functional component', () => { it('updates with a native child', () => { function Foo({ children }) { return children; } var elementBefore =
; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; var elementAfter = ; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'span', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from null to a native child', () => { function Foo({ children }) { return children; } var elementBefore = {null}; var treeBefore = { isComposite: true, displayName: 'Foo', children: [], }; var elementAfter =
; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a native child to null', () => { function Foo({ children }) { return children; } var elementBefore =
; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; var elementAfter = {null}; var treeAfter = { isComposite: true, displayName: 'Foo', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a native child to a composite child', () => { function Bar() { return null; } function Foo({ children }) { return children; } var elementBefore =
; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; var elementAfter = ; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a composite child to a native child', () => { function Bar() { return null; } function Foo({ children }) { return children; } var elementBefore = ; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; var elementAfter =
; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from null to a composite child', () => { function Bar() { return null; } function Foo({ children }) { return children; } var elementBefore = {null}; var treeBefore = { isComposite: true, displayName: 'Foo', children: [], }; var elementAfter = ; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a composite child to null', () => { function Bar() { return null; } function Foo({ children }) { return children; } var elementBefore = ; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; var elementAfter = {null}; var treeAfter = { isComposite: true, displayName: 'Foo', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); }); describe('class component', () => { it('updates with a native child', () => { var Foo = React.createClass({ render() { return this.props.children; }, }); var elementBefore =
; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; var elementAfter = ; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'span', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from null to a native child', () => { var Foo = React.createClass({ render() { return this.props.children; }, }); var elementBefore = {null}; var treeBefore = { isComposite: true, displayName: 'Foo', children: [], }; var elementAfter =
; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a native child to null', () => { var Foo = React.createClass({ render() { return this.props.children; }, }); var elementBefore =
; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; var elementAfter = {null}; var treeAfter = { isComposite: true, displayName: 'Foo', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a native child to a composite child', () => { var Bar = React.createClass({ render() { return null; }, }); var Foo = React.createClass({ render() { return this.props.children; }, }); var elementBefore =
; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; var elementAfter = ; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a composite child to a native child', () => { var Bar = React.createClass({ render() { return null; }, }); var Foo = React.createClass({ render() { return this.props.children; }, }); var elementBefore = ; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; var elementAfter =
; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: false, displayName: 'div', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from null to a composite child', () => { var Bar = React.createClass({ render() { return null; }, }); var Foo = React.createClass({ render() { return this.props.children; }, }); var elementBefore = {null}; var treeBefore = { isComposite: true, displayName: 'Foo', children: [], }; var elementAfter = ; var treeAfter = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); it('updates from a composite child to null', () => { var Bar = React.createClass({ render() { return null; }, }); var Foo = React.createClass({ render() { return this.props.children; }, }); var elementBefore = ; var treeBefore = { isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', children: [], }], }; var elementAfter = {null}; var treeAfter = { isComposite: true, displayName: 'Foo', children: [], }; assertTreeMatches([ [elementBefore, treeBefore], [elementAfter, treeAfter], ]); }); }); }); it('tracks owner correctly', () => { class Foo extends React.Component { render() { return

Hi.

; } } function Bar({children}) { return
{children} Mom
; } // Note that owner is not calculated for text nodes // because they are not created from real elements. var element =
; var tree = { isComposite: false, displayName: 'article', children: [{ isComposite: true, displayName: 'Foo', children: [{ isComposite: true, displayName: 'Bar', ownerDisplayName: 'Foo', children: [{ isComposite: false, displayName: 'div', ownerDisplayName: 'Bar', children: [{ isComposite: false, displayName: 'h1', ownerDisplayName: 'Foo', children: [{ isComposite: false, displayName: '#text', text: 'Hi.', }], }, { isComposite: false, displayName: '#text', text: ' Mom', }], }], }], }], }; assertTreeMatches([element, tree], true); }); });