mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
* 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
242 lines
8.2 KiB
JavaScript
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));
|
|
}
|
|
});
|
|
});
|
|
});
|