Files
react/packages/react-dom/src/__tests__/ReactTreeTraversal-test.js
T
Dan AbramovandGitHub 92b7b172cc Use named exports in more places (#11457)
* Convert EventPlugin{Hub,Registry} to named exports

* Convert EventPluginUtils to named exports

* Convert EventPropagators to named exports

* Convert ReactControlledComponent to named exports

* Convert ReactGenericBatching to named exports

* Convert ReactDOMComponentTree to named exports

* Convert ReactNativeComponentTree to named exports

* Convert ReactNativeRTComponentTree to named exports

* Convert FallbackCompositionState to named exports

* Convert ReactEventEmitterMixin to named exports

* Convert ReactBrowserEventEmitter to named exports

* Convert ReactNativeEventEmitter to named exports

* Convert ReactDOMEventListener to named exports

* Convert DOMMarkupOperations to named exports

* Convert DOMProperty to named exports

* Add suppression for existing Flow violation

Flow didn't see it before.

* Update sizes
2017-11-05 11:58:36 +00:00

242 lines
8.2 KiB
JavaScript

/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
var React = require('react');
// TODO: can we express this test with only public API?
var ReactDOMComponentTree = require('../client/ReactDOMComponentTree');
var ReactTestUtils = require('react-dom/test-utils');
/**
* Ensure that all callbacks are invoked, passing this unique argument.
*/
var ARG = {arg: true};
var ARG2 = {arg2: true};
class ChildComponent extends React.Component {
render() {
return (
<div ref="DIV" id={this.props.id + '__DIV'}>
<div ref="DIV_1" id={this.props.id + '__DIV_1'} />
<div ref="DIV_2" id={this.props.id + '__DIV_2'} />
</div>
);
}
}
class ParentComponent extends React.Component {
render() {
return (
<div ref="P" id="P">
<div ref="P_P1" id="P_P1">
<ChildComponent ref="P_P1_C1" id="P_P1_C1" />
<ChildComponent ref="P_P1_C2" id="P_P1_C2" />
</div>
<div ref="P_OneOff" id="P_OneOff" />
</div>
);
}
}
function renderParentIntoDocument() {
return ReactTestUtils.renderIntoDocument(<ParentComponent />);
}
describe('ReactTreeTraversal', () => {
var ReactTreeTraversal;
var mockFn = jest.fn();
function callback(inst, phase, arg) {
mockFn(ReactDOMComponentTree.getNodeFromInstance(inst).id, phase, arg);
}
function getInst(node) {
return ReactDOMComponentTree.getInstanceFromNode(node);
}
beforeEach(() => {
ReactTreeTraversal = require('shared/ReactTreeTraversal');
mockFn.mockReset();
});
describe('traverseTwoPhase', () => {
it('should not traverse when traversing outside DOM', () => {
ReactTreeTraversal.traverseTwoPhase(null, callback, ARG);
expect(mockFn).not.toHaveBeenCalled();
});
it('should traverse two phase across component boundary', () => {
var parent = renderParentIntoDocument();
var target = getInst(parent.refs.P_P1_C1.refs.DIV_1);
var expectedCalls = [
['P', 'captured', ARG],
['P_P1', 'captured', ARG],
['P_P1_C1__DIV', 'captured', ARG],
['P_P1_C1__DIV_1', 'captured', ARG],
['P_P1_C1__DIV_1', 'bubbled', ARG],
['P_P1_C1__DIV', 'bubbled', ARG],
['P_P1', 'bubbled', ARG],
['P', 'bubbled', ARG],
];
ReactTreeTraversal.traverseTwoPhase(target, callback, ARG);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
it('should traverse two phase at shallowest node', () => {
var parent = renderParentIntoDocument();
var target = getInst(parent.refs.P);
var expectedCalls = [['P', 'captured', ARG], ['P', 'bubbled', ARG]];
ReactTreeTraversal.traverseTwoPhase(target, callback, ARG);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
});
describe('traverseEnterLeave', () => {
it('should not traverse when enter/leaving outside DOM', () => {
ReactTreeTraversal.traverseEnterLeave(null, null, callback, ARG, ARG2);
expect(mockFn).not.toHaveBeenCalled();
});
it('should not traverse if enter/leave the same node', () => {
var parent = renderParentIntoDocument();
var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
var enter = getInst(parent.refs.P_P1_C1.refs.DIV_1);
ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2);
expect(mockFn).not.toHaveBeenCalled();
});
it('should traverse enter/leave to sibling - avoids parent', () => {
var parent = renderParentIntoDocument();
var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
var enter = getInst(parent.refs.P_P1_C1.refs.DIV_2);
var expectedCalls = [
['P_P1_C1__DIV_1', 'bubbled', ARG],
// enter/leave shouldn't fire anything on the parent
['P_P1_C1__DIV_2', 'captured', ARG2],
];
ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
it('should traverse enter/leave to parent - avoids parent', () => {
var parent = renderParentIntoDocument();
var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
var enter = getInst(parent.refs.P_P1_C1.refs.DIV);
var expectedCalls = [['P_P1_C1__DIV_1', 'bubbled', ARG]];
ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
it('should enter from the window', () => {
var parent = renderParentIntoDocument();
var leave = null; // From the window or outside of the React sandbox.
var enter = getInst(parent.refs.P_P1_C1.refs.DIV);
var expectedCalls = [
['P', 'captured', ARG2],
['P_P1', 'captured', ARG2],
['P_P1_C1__DIV', 'captured', ARG2],
];
ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
it('should enter from the window to the shallowest', () => {
var parent = renderParentIntoDocument();
var leave = null; // From the window or outside of the React sandbox.
var enter = getInst(parent.refs.P);
var expectedCalls = [['P', 'captured', ARG2]];
ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
it('should leave to the window', () => {
var parent = renderParentIntoDocument();
var enter = null; // From the window or outside of the React sandbox.
var leave = getInst(parent.refs.P_P1_C1.refs.DIV);
var expectedCalls = [
['P_P1_C1__DIV', 'bubbled', ARG],
['P_P1', 'bubbled', ARG],
['P', 'bubbled', ARG],
];
ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
it('should leave to the window from the shallowest', () => {
var parent = renderParentIntoDocument();
var enter = null; // From the window or outside of the React sandbox.
var leave = getInst(parent.refs.P_P1_C1.refs.DIV);
var expectedCalls = [
['P_P1_C1__DIV', 'bubbled', ARG],
['P_P1', 'bubbled', ARG],
['P', 'bubbled', ARG],
];
ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2);
expect(mockFn.mock.calls).toEqual(expectedCalls);
});
});
describe('getFirstCommonAncestor', () => {
it('should determine the first common ancestor correctly', () => {
var parent = renderParentIntoDocument();
var ancestors = [
// Common ancestor with self is self.
{
one: parent.refs.P_P1_C1.refs.DIV_1,
two: parent.refs.P_P1_C1.refs.DIV_1,
com: parent.refs.P_P1_C1.refs.DIV_1,
},
// Common ancestor with self is self - even if topmost DOM.
{one: parent.refs.P, two: parent.refs.P, com: parent.refs.P},
// Siblings
{
one: parent.refs.P_P1_C1.refs.DIV_1,
two: parent.refs.P_P1_C1.refs.DIV_2,
com: parent.refs.P_P1_C1.refs.DIV,
},
// Common ancestor with parent is the parent.
{
one: parent.refs.P_P1_C1.refs.DIV_1,
two: parent.refs.P_P1_C1.refs.DIV,
com: parent.refs.P_P1_C1.refs.DIV,
},
// Common ancestor with grandparent is the grandparent.
{
one: parent.refs.P_P1_C1.refs.DIV_1,
two: parent.refs.P_P1,
com: parent.refs.P_P1,
},
// Grandparent across subcomponent boundaries.
{
one: parent.refs.P_P1_C1.refs.DIV_1,
two: parent.refs.P_P1_C2.refs.DIV_1,
com: parent.refs.P_P1,
},
// Something deep with something one-off.
{
one: parent.refs.P_P1_C1.refs.DIV_1,
two: parent.refs.P_OneOff,
com: parent.refs.P,
},
];
var i;
for (i = 0; i < ancestors.length; i++) {
var plan = ancestors[i];
var firstCommon = ReactTreeTraversal.getLowestCommonAncestor(
getInst(plan.one),
getInst(plan.two),
);
expect(firstCommon).toBe(getInst(plan.com));
}
});
});
});