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:
CommitSyncScript
2013-06-10 16:18:13 -07:00
committed by Paul O’Shannessy
parent 1be6c592a6
commit ca3f871646
2 changed files with 353 additions and 329 deletions
+94 -85
View File
@@ -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
};
+259 -244
View File
@@ -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);
}
});
});
});