mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Reduce ReactInstanceHandles API Surface Area
Change `ReactInstanceHandles` so that `getFirstCommonAncestorID` and `nextDescendantID` are now private (and documented to be only exposed for unit testing). Also: - Renamed `nextDescendantID` to `getNextDescendantID`. - Renamed `parentID` to `getParentID`. I also organized `ReactInstanceHandles-test` by method names. Functionally, this diff should not change anything.
This commit is contained in:
committed by
Paul O’Shannessy
parent
1be6c592a6
commit
ca3f871646
@@ -82,10 +82,87 @@ function isValidID(id) {
|
||||
* @return {string} ID of the parent, or an empty string.
|
||||
* @private
|
||||
*/
|
||||
function parentID(id) {
|
||||
function getParentID(id) {
|
||||
return id ? id.substr(0, id.lastIndexOf(SEPARATOR)) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
|
||||
* supplied `destinationID`. If they are equal, the ID is returned.
|
||||
*
|
||||
* @param {string} ancestorID ID of an ancestor node of `destinationID`.
|
||||
* @param {string} destinationID ID of the destination node.
|
||||
* @return {string} Next ID on the path from `ancestorID` to `destinationID`.
|
||||
* @private
|
||||
*/
|
||||
function getNextDescendantID(ancestorID, destinationID) {
|
||||
invariant(
|
||||
isValidID(ancestorID) && isValidID(destinationID),
|
||||
'getNextDescendantID(%s, %s): Received an invalid React DOM ID.',
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
var longestCommonID = getFirstCommonAncestorID(
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
invariant(
|
||||
longestCommonID === ancestorID,
|
||||
'getNextDescendantID(...): React has made an invalid assumption about ' +
|
||||
'the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.',
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
if (ancestorID === destinationID) {
|
||||
return ancestorID;
|
||||
}
|
||||
// Skip over the ancestor and the immediate separator. Traverse until we hit
|
||||
// another separator or we reach the end of `destinationID`.
|
||||
var start = ancestorID.length + SEPARATOR_LENGTH;
|
||||
for (var i = start; i < destinationID.length; i++) {
|
||||
if (isMarker(destinationID, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return destinationID.substr(0, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest common ancestor ID of two IDs.
|
||||
*
|
||||
* Using this ID scheme, the nearest common ancestor ID is the longest common
|
||||
* prefix of the two IDs that immediately preceded a "marker" in both strings.
|
||||
*
|
||||
* @param {string} oneID
|
||||
* @param {string} twoID
|
||||
* @return {string} Nearest common ancestor ID, or the empty string if none.
|
||||
* @private
|
||||
*/
|
||||
function getFirstCommonAncestorID(oneID, twoID) {
|
||||
var minLength = Math.min(oneID.length, twoID.length);
|
||||
if (minLength === 0) {
|
||||
return '';
|
||||
}
|
||||
var lastCommonMarkerIndex = 0;
|
||||
// Use `<=` to traverse until the "EOL" of the shorter string.
|
||||
for (var i = 0; i <= minLength; i++) {
|
||||
if (isMarker(oneID, i) && isMarker(twoID, i)) {
|
||||
lastCommonMarkerIndex = i;
|
||||
} else if (oneID.charAt(i) !== twoID.charAt(i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
|
||||
invariant(
|
||||
isValidID(longestCommonID),
|
||||
'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
|
||||
oneID,
|
||||
twoID,
|
||||
longestCommonID
|
||||
);
|
||||
return longestCommonID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the parent path between two IDs (either up or down). The IDs must
|
||||
* not be the same, and there must exist a parent path between them.
|
||||
@@ -105,7 +182,7 @@ function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
|
||||
'traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.',
|
||||
start
|
||||
);
|
||||
var ancestorID = ReactInstanceHandles.getFirstCommonAncestorID(start, stop);
|
||||
var ancestorID = getFirstCommonAncestorID(start, stop);
|
||||
var traverseUp = ancestorID === stop;
|
||||
invariant(
|
||||
traverseUp || ancestorID === start,
|
||||
@@ -116,7 +193,7 @@ function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
|
||||
);
|
||||
// Traverse from `start` to `stop` one depth at a time.
|
||||
var depth = 0;
|
||||
var traverse = traverseUp ? parentID : ReactInstanceHandles.nextDescendantID;
|
||||
var traverse = traverseUp ? getParentID : getNextDescendantID;
|
||||
for (var id = start; /* until break */; id = traverse(id, stop)) {
|
||||
if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
|
||||
cb(id, traverseUp, arg);
|
||||
@@ -208,42 +285,6 @@ var ReactInstanceHandles = {
|
||||
// Effectively: return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the nearest common ancestor ID of two IDs.
|
||||
*
|
||||
* Using this ID scheme, the nearest common ancestor ID is the longest common
|
||||
* prefix of the two IDs that immediately preceded a "marker" in both strings.
|
||||
*
|
||||
* @param {string} oneID
|
||||
* @param {string} twoID
|
||||
* @return {string} Nearest common ancestor ID, or the empty string if none.
|
||||
* @internal
|
||||
*/
|
||||
getFirstCommonAncestorID: function(oneID, twoID) {
|
||||
var minLength = Math.min(oneID.length, twoID.length);
|
||||
if (minLength === 0) {
|
||||
return '';
|
||||
}
|
||||
var lastCommonMarkerIndex = 0;
|
||||
// Use `<=` to traverse until the "EOL" of the shorter string.
|
||||
for (var i = 0; i <= minLength; i++) {
|
||||
if (isMarker(oneID, i) && isMarker(twoID, i)) {
|
||||
lastCommonMarkerIndex = i;
|
||||
} else if (oneID.charAt(i) !== twoID.charAt(i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
|
||||
invariant(
|
||||
isValidID(longestCommonID),
|
||||
'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
|
||||
oneID,
|
||||
twoID,
|
||||
longestCommonID
|
||||
);
|
||||
return longestCommonID;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the DOM ID of the React component that is the root of the tree that
|
||||
* contains the React component with the supplied DOM ID.
|
||||
@@ -272,15 +313,12 @@ var ReactInstanceHandles = {
|
||||
* @internal
|
||||
*/
|
||||
traverseEnterLeave: function(leaveID, enterID, cb, upArg, downArg) {
|
||||
var longestCommonID = ReactInstanceHandles.getFirstCommonAncestorID(
|
||||
leaveID,
|
||||
enterID
|
||||
);
|
||||
if (longestCommonID !== leaveID) {
|
||||
traverseParentPath(leaveID, longestCommonID, cb, upArg, false, true);
|
||||
var ancestorID = getFirstCommonAncestorID(leaveID, enterID);
|
||||
if (ancestorID !== leaveID) {
|
||||
traverseParentPath(leaveID, ancestorID, cb, upArg, false, true);
|
||||
}
|
||||
if (longestCommonID !== enterID) {
|
||||
traverseParentPath(longestCommonID, enterID, cb, downArg, true, false);
|
||||
if (ancestorID !== enterID) {
|
||||
traverseParentPath(ancestorID, enterID, cb, downArg, true, false);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -302,45 +340,16 @@ var ReactInstanceHandles = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
|
||||
* supplied `destinationID`. If they are equal, the ID is returned.
|
||||
*
|
||||
* @param {string} ancestorID ID of an ancestor node of `destinationID`.
|
||||
* @param {string} destinationID ID of the destination node.
|
||||
* @return {string} Next ID on the path from `ancestorID` to `destinationID`.
|
||||
* @internal
|
||||
* Exposed for unit testing.
|
||||
* @private
|
||||
*/
|
||||
nextDescendantID: function(ancestorID, destinationID) {
|
||||
invariant(
|
||||
isValidID(ancestorID) && isValidID(destinationID),
|
||||
'nextDescendantID(%s, %s): Received an invalid React DOM ID.',
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
var longestCommonID = ReactInstanceHandles.getFirstCommonAncestorID(
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
invariant(
|
||||
longestCommonID === ancestorID,
|
||||
'nextDescendantID(...): React has made an invalid assumption about the ' +
|
||||
'DOM hierarchy. Expected `%s` to be an ancestor of `%s`.',
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
if (ancestorID === destinationID) {
|
||||
return ancestorID;
|
||||
}
|
||||
// Skip over the ancestor and the immediate separator. Traverse until we hit
|
||||
// another separator or we reach the end of `destinationID`.
|
||||
var start = ancestorID.length + SEPARATOR_LENGTH;
|
||||
for (var i = start; i < destinationID.length; i++) {
|
||||
if (isMarker(destinationID, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return destinationID.substr(0, i);
|
||||
}
|
||||
_getFirstCommonAncestorID: getFirstCommonAncestorID,
|
||||
|
||||
/**
|
||||
* Exposed for unit testing.
|
||||
* @private
|
||||
*/
|
||||
_getNextDescendantID: getNextDescendantID
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -19,12 +19,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
require('mock-modules')
|
||||
.dontMock('ReactInstanceHandles');
|
||||
|
||||
var React = require('React');
|
||||
var ReactComponent = require('ReactComponent');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ReactTestUtils = require('ReactTestUtils');
|
||||
|
||||
var reactComponentExpect= require('reactComponentExpect');
|
||||
@@ -64,259 +59,279 @@ function renderParentIntoDocument() {
|
||||
return ReactTestUtils.renderIntoDocument(<ParentComponent />);
|
||||
}
|
||||
|
||||
var aggregatedArgs = [];
|
||||
function argAggregator(id, isUp, arg) {
|
||||
aggregatedArgs.push({
|
||||
id: id,
|
||||
isUp: isUp,
|
||||
arg: arg
|
||||
});
|
||||
}
|
||||
describe('ReactInstanceHandles', function() {
|
||||
var ReactInstanceHandles;
|
||||
|
||||
var nextDescendantID;
|
||||
var traverseTwoPhase;
|
||||
var traverseEnterLeave;
|
||||
var getFirstCommonAncestorID;
|
||||
var aggregatedArgs;
|
||||
function argAggregator(id, isUp, arg) {
|
||||
aggregatedArgs.push({
|
||||
id: id,
|
||||
isUp: isUp,
|
||||
arg: arg
|
||||
});
|
||||
}
|
||||
|
||||
describe('ReactInstanceHandles traversal', function() {
|
||||
beforeEach(function() {
|
||||
require('mock-modules').dumpCache();
|
||||
aggregatedArgs = [];
|
||||
ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
nextDescendantID = ReactInstanceHandles.nextDescendantID;
|
||||
getFirstCommonAncestorID = ReactInstanceHandles.getFirstCommonAncestorID;
|
||||
traverseTwoPhase = ReactInstanceHandles.traverseTwoPhase;
|
||||
traverseEnterLeave = ReactInstanceHandles.traverseEnterLeave;
|
||||
aggregatedArgs = [];
|
||||
});
|
||||
|
||||
it("should return next descendent from window", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
expect(nextDescendantID('', parent.refs.P_P1._rootNodeID)).toBe(
|
||||
parent.refs.P._rootNodeID
|
||||
);
|
||||
describe('isRenderedByReact', function() {
|
||||
it('should not crash on text nodes', function() {
|
||||
expect(function() {
|
||||
ReactInstanceHandles.isRenderedByReact(document.createTextNode('yolo'));
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return window for next descendent towards window", function() {
|
||||
expect(nextDescendantID('', '')).toBe('');
|
||||
describe('getReactRootIDFromNodeID', function() {
|
||||
it('should support strings', function() {
|
||||
var test = '.reactRoot[s_0_1][0]..[1]';
|
||||
var expected = '.reactRoot[s_0_1]';
|
||||
var actual = ReactInstanceHandles.getReactRootIDFromNodeID(test);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it("should return self for next descendent towards self", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
expect(
|
||||
nextDescendantID(
|
||||
parent.refs.P_P1._rootNodeID,
|
||||
parent.refs.P_P1._rootNodeID
|
||||
)
|
||||
).toBe(parent.refs.P_P1._rootNodeID);
|
||||
describe('traverseTwoPhase', function() {
|
||||
it("should not traverse when traversing outside DOM", function() {
|
||||
var targetID = '';
|
||||
var expectedAggregation = [];
|
||||
ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should traverse two phase across component boundary", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var targetID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: false, arg: ARG},
|
||||
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should traverse two phase at shallowest node", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var targetID = parent.refs.P._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not traverse when traversing outside DOM", function() {
|
||||
var targetID = '';
|
||||
var expectedAggregation = [];
|
||||
traverseTwoPhase(targetID, argAggregator, ARG);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should not traverse when enter/leaving outside DOM", function() {
|
||||
var targetID = '';
|
||||
var expectedAggregation = [];
|
||||
traverseEnterLeave(targetID, targetID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should not traverse if enter/leave the same node", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var expectedAggregation = [];
|
||||
traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should traverse two phase across component boundary", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var targetID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: false, arg: ARG},
|
||||
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
traverseTwoPhase(targetID, argAggregator, ARG);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should traverse two phase at shallowest node", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var targetID = parent.refs.P._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
traverseTwoPhase(targetID, argAggregator, ARG);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should traverse enter/leave to sibling - avoids parent", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV_2._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
|
||||
// enter/leave shouldn't fire antyhing on the parent
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_2._rootNodeID, isUp: false, arg: ARG2}
|
||||
];
|
||||
traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should traverse enter/leave to parent - avoids parent", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should enter from the window", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = ''; // From the window or outside of the React sandbox.
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG2},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG2}
|
||||
];
|
||||
traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should enter from the window to the shallowest", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = ''; // From the window or outside of the React sandbox.
|
||||
var enterID = parent.refs.P._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2}
|
||||
];
|
||||
traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should leave to the window", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var enterID = ''; // From the window or outside of the React sandbox.
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should leave to the window from the shallowest", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var enterID = ''; // From the window or outside of the React sandbox.
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should determine the first common ancestor correctly", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var ancestors = [
|
||||
// Common ancestor from window to deep element is ''.
|
||||
{ one: {_rootNodeID: ''},
|
||||
two: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
com: {_rootNodeID: ''}
|
||||
},
|
||||
// Same as previous - reversed direction.
|
||||
{ one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: {_rootNodeID: ''},
|
||||
com: {_rootNodeID: ''}
|
||||
},
|
||||
// Common ancestor from window to shallow id is ''.
|
||||
{ one: parent.refs.P,
|
||||
two: {_rootNodeID: ''},
|
||||
com: {_rootNodeID: ''}
|
||||
},
|
||||
// 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_C1,
|
||||
com: parent.refs.P_P1_C1
|
||||
},
|
||||
// Grantparent 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 = getFirstCommonAncestorID(
|
||||
plan.one._rootNodeID,
|
||||
plan.two._rootNodeID
|
||||
describe('traverseEnterLeave', function() {
|
||||
it("should not traverse when enter/leaving outside DOM", function() {
|
||||
var targetID = '';
|
||||
var expectedAggregation = [];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
targetID, targetID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(firstCommon).toBe(plan.com._rootNodeID);
|
||||
}
|
||||
});
|
||||
});
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
describe('ReactInstanceHandles.getReactRootIDFromNodeID', function() {
|
||||
it('should support strings', function() {
|
||||
var test = '.reactRoot[s_0_1][0]..[1]';
|
||||
var expected = '.reactRoot[s_0_1]';
|
||||
var actual = ReactInstanceHandles.getReactRootIDFromNodeID(test);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
it("should not traverse if enter/leave the same node", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var expectedAggregation = [];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
leaveID, enterID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
describe('ReactInstanceHandles.isRenderedByReact', function() {
|
||||
it('should not crash on text nodes', function() {
|
||||
expect(function() {
|
||||
ReactInstanceHandles.isRenderedByReact(document.createTextNode('yolo'))
|
||||
}).not.toThrow();
|
||||
it("should traverse enter/leave to sibling - avoids parent", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV_2._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
|
||||
// enter/leave shouldn't fire antyhing on the parent
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_2._rootNodeID, isUp: false, arg: ARG2}
|
||||
];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
leaveID, enterID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should traverse enter/leave to parent - avoids parent", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
leaveID, enterID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should enter from the window", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = ''; // From the window or outside of the React sandbox.
|
||||
var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG2},
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG2}
|
||||
];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
leaveID, enterID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should enter from the window to the shallowest", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var leaveID = ''; // From the window or outside of the React sandbox.
|
||||
var enterID = parent.refs.P._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2}
|
||||
];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
leaveID, enterID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should leave to the window", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var enterID = ''; // From the window or outside of the React sandbox.
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
leaveID, enterID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
|
||||
it("should leave to the window from the shallowest", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var enterID = ''; // From the window or outside of the React sandbox.
|
||||
var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
|
||||
var expectedAggregation = [
|
||||
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
|
||||
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
|
||||
];
|
||||
ReactInstanceHandles.traverseEnterLeave(
|
||||
leaveID, enterID, argAggregator, ARG, ARG2
|
||||
);
|
||||
expect(aggregatedArgs).toEqual(expectedAggregation);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNextDescendantID', function() {
|
||||
it("should return next descendent from window", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
expect(
|
||||
ReactInstanceHandles._getNextDescendantID(
|
||||
'',
|
||||
parent.refs.P_P1._rootNodeID
|
||||
)
|
||||
).toBe(parent.refs.P._rootNodeID);
|
||||
});
|
||||
|
||||
it("should return window for next descendent towards window", function() {
|
||||
expect(ReactInstanceHandles._getNextDescendantID('', '')).toBe('');
|
||||
});
|
||||
|
||||
it("should return self for next descendent towards self", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
expect(
|
||||
ReactInstanceHandles._getNextDescendantID(
|
||||
parent.refs.P_P1._rootNodeID,
|
||||
parent.refs.P_P1._rootNodeID
|
||||
)
|
||||
).toBe(parent.refs.P_P1._rootNodeID);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFirstCommonAncestorID', function() {
|
||||
it("should determine the first common ancestor correctly", function() {
|
||||
var parent = renderParentIntoDocument();
|
||||
var ancestors = [
|
||||
// Common ancestor from window to deep element is ''.
|
||||
{ one: {_rootNodeID: ''},
|
||||
two: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
com: {_rootNodeID: ''}
|
||||
},
|
||||
// Same as previous - reversed direction.
|
||||
{ one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: {_rootNodeID: ''},
|
||||
com: {_rootNodeID: ''}
|
||||
},
|
||||
// Common ancestor from window to shallow id is ''.
|
||||
{ one: parent.refs.P,
|
||||
two: {_rootNodeID: ''},
|
||||
com: {_rootNodeID: ''}
|
||||
},
|
||||
// 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_C1,
|
||||
com: parent.refs.P_P1_C1
|
||||
},
|
||||
// Grantparent 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 = ReactInstanceHandles._getFirstCommonAncestorID(
|
||||
plan.one._rootNodeID,
|
||||
plan.two._rootNodeID
|
||||
);
|
||||
expect(firstCommon).toBe(plan.com._rootNodeID);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user