@samitouri / QOS-React-2 / commits / ca3f871646
Fork 0

The library for web and native user interfaces.

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.

CommitSyncScript committed Jun 10, 2013 at 16:16 UTC ca3f87164634b40678f67d24076ee8f39dddeaa2
2 files changed +340 -316
src/core/ReactInstanceHandles.js
+94 -85
@@ -82,10 +82,87 @@ function isValidID(id) {
82 * @return {string} ID of the parent, or an empty string.
83 * @private
84 */
85 -function parentID(id) {
85 +function getParentID(id) {
86 return id ? id.substr(0, id.lastIndexOf(SEPARATOR)) : '';
87 }
88
89 +/**
90 + * Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
91 + * supplied `destinationID`. If they are equal, the ID is returned.
92 + *
93 + * @param {string} ancestorID ID of an ancestor node of `destinationID`.
94 + * @param {string} destinationID ID of the destination node.
95 + * @return {string} Next ID on the path from `ancestorID` to `destinationID`.
96 + * @private
97 + */
98 +function getNextDescendantID(ancestorID, destinationID) {
99 + invariant(
100 + isValidID(ancestorID) && isValidID(destinationID),
101 + 'getNextDescendantID(%s, %s): Received an invalid React DOM ID.',
102 + ancestorID,
103 + destinationID
104 + );
105 + var longestCommonID = getFirstCommonAncestorID(
106 + ancestorID,
107 + destinationID
108 + );
109 + invariant(
110 + longestCommonID === ancestorID,
111 + 'getNextDescendantID(...): React has made an invalid assumption about ' +
112 + 'the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.',
113 + ancestorID,
114 + destinationID
115 + );
116 + if (ancestorID === destinationID) {
117 + return ancestorID;
118 + }
119 + // Skip over the ancestor and the immediate separator. Traverse until we hit
120 + // another separator or we reach the end of `destinationID`.
121 + var start = ancestorID.length + SEPARATOR_LENGTH;
122 + for (var i = start; i < destinationID.length; i++) {
123 + if (isMarker(destinationID, i)) {
124 + break;
125 + }
126 + }
127 + return destinationID.substr(0, i);
128 +}
129 +
130 +/**
131 + * Gets the nearest common ancestor ID of two IDs.
132 + *
133 + * Using this ID scheme, the nearest common ancestor ID is the longest common
134 + * prefix of the two IDs that immediately preceded a "marker" in both strings.
135 + *
136 + * @param {string} oneID
137 + * @param {string} twoID
138 + * @return {string} Nearest common ancestor ID, or the empty string if none.
139 + * @private
140 + */
141 +function getFirstCommonAncestorID(oneID, twoID) {
142 + var minLength = Math.min(oneID.length, twoID.length);
143 + if (minLength === 0) {
144 + return '';
145 + }
146 + var lastCommonMarkerIndex = 0;
147 + // Use `<=` to traverse until the "EOL" of the shorter string.
148 + for (var i = 0; i <= minLength; i++) {
149 + if (isMarker(oneID, i) && isMarker(twoID, i)) {
150 + lastCommonMarkerIndex = i;
151 + } else if (oneID.charAt(i) !== twoID.charAt(i)) {
152 + break;
153 + }
154 + }
155 + var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
156 + invariant(
157 + isValidID(longestCommonID),
158 + 'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
159 + oneID,
160 + twoID,
161 + longestCommonID
162 + );
163 + return longestCommonID;
164 +}
165 +
166 /**
167 * Traverses the parent path between two IDs (either up or down). The IDs must
168 * 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) {
182 'traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.',
183 start
184 );
108 - var ancestorID = ReactInstanceHandles.getFirstCommonAncestorID(start, stop);
185 + var ancestorID = getFirstCommonAncestorID(start, stop);
186 var traverseUp = ancestorID === stop;
187 invariant(
188 traverseUp || ancestorID === start,
@@ -116,7 +193,7 @@ function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
193 );
194 // Traverse from `start` to `stop` one depth at a time.
195 var depth = 0;
119 - var traverse = traverseUp ? parentID : ReactInstanceHandles.nextDescendantID;
196 + var traverse = traverseUp ? getParentID : getNextDescendantID;
197 for (var id = start; /* until break */; id = traverse(id, stop)) {
198 if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
199 cb(id, traverseUp, arg);
@@ -208,42 +285,6 @@ var ReactInstanceHandles = {
285 // Effectively: return null;
286 },
287
211 - /**
212 - * Gets the nearest common ancestor ID of two IDs.
213 - *
214 - * Using this ID scheme, the nearest common ancestor ID is the longest common
215 - * prefix of the two IDs that immediately preceded a "marker" in both strings.
216 - *
217 - * @param {string} oneID
218 - * @param {string} twoID
219 - * @return {string} Nearest common ancestor ID, or the empty string if none.
220 - * @internal
221 - */
222 - getFirstCommonAncestorID: function(oneID, twoID) {
223 - var minLength = Math.min(oneID.length, twoID.length);
224 - if (minLength === 0) {
225 - return '';
226 - }
227 - var lastCommonMarkerIndex = 0;
228 - // Use `<=` to traverse until the "EOL" of the shorter string.
229 - for (var i = 0; i <= minLength; i++) {
230 - if (isMarker(oneID, i) && isMarker(twoID, i)) {
231 - lastCommonMarkerIndex = i;
232 - } else if (oneID.charAt(i) !== twoID.charAt(i)) {
233 - break;
234 - }
235 - }
236 - var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
237 - invariant(
238 - isValidID(longestCommonID),
239 - 'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
240 - oneID,
241 - twoID,
242 - longestCommonID
243 - );
244 - return longestCommonID;
245 - },
246 -
288 /**
289 * Gets the DOM ID of the React component that is the root of the tree that
290 * contains the React component with the supplied DOM ID.
@@ -272,15 +313,12 @@ var ReactInstanceHandles = {
313 * @internal
314 */
315 traverseEnterLeave: function(leaveID, enterID, cb, upArg, downArg) {
275 - var longestCommonID = ReactInstanceHandles.getFirstCommonAncestorID(
276 - leaveID,
277 - enterID
278 - );
279 - if (longestCommonID !== leaveID) {
280 - traverseParentPath(leaveID, longestCommonID, cb, upArg, false, true);
316 + var ancestorID = getFirstCommonAncestorID(leaveID, enterID);
317 + if (ancestorID !== leaveID) {
318 + traverseParentPath(leaveID, ancestorID, cb, upArg, false, true);
319 }
282 - if (longestCommonID !== enterID) {
283 - traverseParentPath(longestCommonID, enterID, cb, downArg, true, false);
320 + if (ancestorID !== enterID) {
321 + traverseParentPath(ancestorID, enterID, cb, downArg, true, false);
322 }
323 },
324
@@ -302,45 +340,16 @@ var ReactInstanceHandles = {
340 },
341
342 /**
305 - * Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
306 - * supplied `destinationID`. If they are equal, the ID is returned.
307 - *
308 - * @param {string} ancestorID ID of an ancestor node of `destinationID`.
309 - * @param {string} destinationID ID of the destination node.
310 - * @return {string} Next ID on the path from `ancestorID` to `destinationID`.
311 - * @internal
343 + * Exposed for unit testing.
344 + * @private
345 */
313 - nextDescendantID: function(ancestorID, destinationID) {
314 - invariant(
315 - isValidID(ancestorID) && isValidID(destinationID),
316 - 'nextDescendantID(%s, %s): Received an invalid React DOM ID.',
317 - ancestorID,
318 - destinationID
319 - );
320 - var longestCommonID = ReactInstanceHandles.getFirstCommonAncestorID(
321 - ancestorID,
322 - destinationID
323 - );
324 - invariant(
325 - longestCommonID === ancestorID,
326 - 'nextDescendantID(...): React has made an invalid assumption about the ' +
327 - 'DOM hierarchy. Expected `%s` to be an ancestor of `%s`.',
328 - ancestorID,
329 - destinationID
330 - );
331 - if (ancestorID === destinationID) {
332 - return ancestorID;
333 - }
334 - // Skip over the ancestor and the immediate separator. Traverse until we hit
335 - // another separator or we reach the end of `destinationID`.
336 - var start = ancestorID.length + SEPARATOR_LENGTH;
337 - for (var i = start; i < destinationID.length; i++) {
338 - if (isMarker(destinationID, i)) {
339 - break;
340 - }
341 - }
342 - return destinationID.substr(0, i);
343 - }
346 + _getFirstCommonAncestorID: getFirstCommonAncestorID,
347 +
348 + /**
349 + * Exposed for unit testing.
350 + * @private
351 + */
352 + _getNextDescendantID: getNextDescendantID
353
354 };
355
src/core/__tests__/ReactInstanceHandles-test.js
+246 -231
@@ -19,12 +19,7 @@
19
20 "use strict";
21
22 -require('mock-modules')
23 - .dontMock('ReactInstanceHandles');
24 -
22 var React = require('React');
26 -var ReactComponent = require('ReactComponent');
27 -var ReactInstanceHandles = require('ReactInstanceHandles');
23 var ReactTestUtils = require('ReactTestUtils');
24
25 var reactComponentExpect= require('reactComponentExpect');
@@ -64,259 +59,279 @@ function renderParentIntoDocument() {
59 return ReactTestUtils.renderIntoDocument(<ParentComponent />);
60 }
61
67 -var aggregatedArgs = [];
68 -function argAggregator(id, isUp, arg) {
69 - aggregatedArgs.push({
70 - id: id,
71 - isUp: isUp,
72 - arg: arg
73 - });
74 -}
62 +describe('ReactInstanceHandles', function() {
63 + var ReactInstanceHandles;
64
76 -var nextDescendantID;
77 -var traverseTwoPhase;
78 -var traverseEnterLeave;
79 -var getFirstCommonAncestorID;
65 + var aggregatedArgs;
66 + function argAggregator(id, isUp, arg) {
67 + aggregatedArgs.push({
68 + id: id,
69 + isUp: isUp,
70 + arg: arg
71 + });
72 + }
73
81 -describe('ReactInstanceHandles traversal', function() {
74 beforeEach(function() {
83 - require('mock-modules').dumpCache();
84 - aggregatedArgs = [];
75 ReactInstanceHandles = require('ReactInstanceHandles');
86 - nextDescendantID = ReactInstanceHandles.nextDescendantID;
87 - getFirstCommonAncestorID = ReactInstanceHandles.getFirstCommonAncestorID;
88 - traverseTwoPhase = ReactInstanceHandles.traverseTwoPhase;
89 - traverseEnterLeave = ReactInstanceHandles.traverseEnterLeave;
76 + aggregatedArgs = [];
77 });
78
92 - it("should return next descendent from window", function() {
93 - var parent = renderParentIntoDocument();
94 - expect(nextDescendantID('', parent.refs.P_P1._rootNodeID)).toBe(
95 - parent.refs.P._rootNodeID
96 - );
79 + describe('isRenderedByReact', function() {
80 + it('should not crash on text nodes', function() {
81 + expect(function() {
82 + ReactInstanceHandles.isRenderedByReact(document.createTextNode('yolo'));
83 + }).not.toThrow();
84 + });
85 });
86
99 - it("should return window for next descendent towards window", function() {
100 - expect(nextDescendantID('', '')).toBe('');
87 + describe('getReactRootIDFromNodeID', function() {
88 + it('should support strings', function() {
89 + var test = '.reactRoot[s_0_1][0]..[1]';
90 + var expected = '.reactRoot[s_0_1]';
91 + var actual = ReactInstanceHandles.getReactRootIDFromNodeID(test);
92 + expect(actual).toEqual(expected);
93 + });
94 });
95
103 - it("should return self for next descendent towards self", function() {
104 - var parent = renderParentIntoDocument();
105 - expect(
106 - nextDescendantID(
107 - parent.refs.P_P1._rootNodeID,
108 - parent.refs.P_P1._rootNodeID
109 - )
110 - ).toBe(parent.refs.P_P1._rootNodeID);
111 - });
96 + describe('traverseTwoPhase', function() {
97 + it("should not traverse when traversing outside DOM", function() {
98 + var targetID = '';
99 + var expectedAggregation = [];
100 + ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
101 + expect(aggregatedArgs).toEqual(expectedAggregation);
102 + });
103
113 - it("should not traverse when traversing outside DOM", function() {
114 - var targetID = '';
115 - var expectedAggregation = [];
116 - traverseTwoPhase(targetID, argAggregator, ARG);
117 - expect(aggregatedArgs).toEqual(expectedAggregation);
118 - });
104 + it("should traverse two phase across component boundary", function() {
105 + var parent = renderParentIntoDocument();
106 + var targetID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
107 + var expectedAggregation = [
108 + {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
109 + {id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG},
110 + {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG},
111 + {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: false, arg: ARG},
112
120 - it("should not traverse when enter/leaving outside DOM", function() {
121 - var targetID = '';
122 - var expectedAggregation = [];
123 - traverseEnterLeave(targetID, targetID, argAggregator, ARG, ARG2);
124 - expect(aggregatedArgs).toEqual(expectedAggregation);
125 - });
113 + {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
114 + {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
115 + {id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
116 + {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
117 + ];
118 + ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
119 + expect(aggregatedArgs).toEqual(expectedAggregation);
120 + });
121
127 - it("should not traverse if enter/leave the same node", function() {
128 - var parent = renderParentIntoDocument();
129 - var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
130 - var enterID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
131 - var expectedAggregation = [];
132 - traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
133 - expect(aggregatedArgs).toEqual(expectedAggregation);
122 + it("should traverse two phase at shallowest node", function() {
123 + var parent = renderParentIntoDocument();
124 + var targetID = parent.refs.P._rootNodeID;
125 + var expectedAggregation = [
126 + {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
127 + {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
128 + ];
129 + ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
130 + expect(aggregatedArgs).toEqual(expectedAggregation);
131 + });
132 });
133
136 - it("should traverse two phase across component boundary", function() {
137 - var parent = renderParentIntoDocument();
138 - var targetID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
139 - var expectedAggregation = [
140 - {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
141 - {id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG},
142 - {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG},
143 - {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: false, arg: ARG},
134 + describe('traverseEnterLeave', function() {
135 + it("should not traverse when enter/leaving outside DOM", function() {
136 + var targetID = '';
137 + var expectedAggregation = [];
138 + ReactInstanceHandles.traverseEnterLeave(
139 + targetID, targetID, argAggregator, ARG, ARG2
140 + );
141 + expect(aggregatedArgs).toEqual(expectedAggregation);
142 + });
143
145 - {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
146 - {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
147 - {id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
148 - {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
149 - ];
150 - traverseTwoPhase(targetID, argAggregator, ARG);
151 - expect(aggregatedArgs).toEqual(expectedAggregation);
152 - });
144 + it("should not traverse if enter/leave the same node", function() {
145 + var parent = renderParentIntoDocument();
146 + var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
147 + var enterID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
148 + var expectedAggregation = [];
149 + ReactInstanceHandles.traverseEnterLeave(
150 + leaveID, enterID, argAggregator, ARG, ARG2
151 + );
152 + expect(aggregatedArgs).toEqual(expectedAggregation);
153 + });
154
154 - it("should traverse two phase at shallowest node", function() {
155 - var parent = renderParentIntoDocument();
156 - var targetID = parent.refs.P._rootNodeID;
157 - var expectedAggregation = [
158 - {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
159 - {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
160 - ];
161 - traverseTwoPhase(targetID, argAggregator, ARG);
162 - expect(aggregatedArgs).toEqual(expectedAggregation);
163 - });
155 + it("should traverse enter/leave to sibling - avoids parent", function() {
156 + var parent = renderParentIntoDocument();
157 + var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
158 + var enterID = parent.refs.P_P1_C1.refs.DIV_2._rootNodeID;
159 + var expectedAggregation = [
160 + {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
161 + // enter/leave shouldn't fire antyhing on the parent
162 + {id: parent.refs.P_P1_C1.refs.DIV_2._rootNodeID, isUp: false, arg: ARG2}
163 + ];
164 + ReactInstanceHandles.traverseEnterLeave(
165 + leaveID, enterID, argAggregator, ARG, ARG2
166 + );
167 + expect(aggregatedArgs).toEqual(expectedAggregation);
168 + });
169
165 - it("should traverse enter/leave to sibling - avoids parent", function() {
166 - var parent = renderParentIntoDocument();
167 - var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
168 - var enterID = parent.refs.P_P1_C1.refs.DIV_2._rootNodeID;
169 - var expectedAggregation = [
170 - {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
171 - // enter/leave shouldn't fire antyhing on the parent
172 - {id: parent.refs.P_P1_C1.refs.DIV_2._rootNodeID, isUp: false, arg: ARG2}
173 - ];
174 - traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
175 - expect(aggregatedArgs).toEqual(expectedAggregation);
176 - });
170 + it("should traverse enter/leave to parent - avoids parent", function() {
171 + var parent = renderParentIntoDocument();
172 + var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
173 + var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
174 + var expectedAggregation = [
175 + {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG}
176 + ];
177 + ReactInstanceHandles.traverseEnterLeave(
178 + leaveID, enterID, argAggregator, ARG, ARG2
179 + );
180 + expect(aggregatedArgs).toEqual(expectedAggregation);
181 + });
182
178 - it("should traverse enter/leave to parent - avoids parent", function() {
179 - var parent = renderParentIntoDocument();
180 - var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
181 - var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
182 - var expectedAggregation = [
183 - {id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG}
184 - ];
185 - traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
186 - expect(aggregatedArgs).toEqual(expectedAggregation);
187 - });
183 + it("should enter from the window", function() {
184 + var parent = renderParentIntoDocument();
185 + var leaveID = ''; // From the window or outside of the React sandbox.
186 + var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
187 + var expectedAggregation = [
188 + {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2},
189 + {id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG2},
190 + {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG2}
191 + ];
192 + ReactInstanceHandles.traverseEnterLeave(
193 + leaveID, enterID, argAggregator, ARG, ARG2
194 + );
195 + expect(aggregatedArgs).toEqual(expectedAggregation);
196 + });
197
189 - it("should enter from the window", function() {
190 - var parent = renderParentIntoDocument();
191 - var leaveID = ''; // From the window or outside of the React sandbox.
192 - var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
193 - var expectedAggregation = [
194 - {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2},
195 - {id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG2},
196 - {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG2}
197 - ];
198 - traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
199 - expect(aggregatedArgs).toEqual(expectedAggregation);
200 - });
198 + it("should enter from the window to the shallowest", function() {
199 + var parent = renderParentIntoDocument();
200 + var leaveID = ''; // From the window or outside of the React sandbox.
201 + var enterID = parent.refs.P._rootNodeID;
202 + var expectedAggregation = [
203 + {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2}
204 + ];
205 + ReactInstanceHandles.traverseEnterLeave(
206 + leaveID, enterID, argAggregator, ARG, ARG2
207 + );
208 + expect(aggregatedArgs).toEqual(expectedAggregation);
209 + });
210
202 - it("should enter from the window to the shallowest", function() {
203 - var parent = renderParentIntoDocument();
204 - var leaveID = ''; // From the window or outside of the React sandbox.
205 - var enterID = parent.refs.P._rootNodeID;
206 - var expectedAggregation = [
207 - {id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2}
208 - ];
209 - traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
210 - expect(aggregatedArgs).toEqual(expectedAggregation);
211 - });
211 + it("should leave to the window", function() {
212 + var parent = renderParentIntoDocument();
213 + var enterID = ''; // From the window or outside of the React sandbox.
214 + var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
215 + var expectedAggregation = [
216 + {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
217 + {id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
218 + {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
219 + ];
220 + ReactInstanceHandles.traverseEnterLeave(
221 + leaveID, enterID, argAggregator, ARG, ARG2
222 + );
223 + expect(aggregatedArgs).toEqual(expectedAggregation);
224 + });
225
213 - it("should leave to the window", function() {
214 - var parent = renderParentIntoDocument();
215 - var enterID = ''; // From the window or outside of the React sandbox.
216 - var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
217 - var expectedAggregation = [
218 - {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
219 - {id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
220 - {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
221 - ];
222 - traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
223 - expect(aggregatedArgs).toEqual(expectedAggregation);
226 + it("should leave to the window from the shallowest", function() {
227 + var parent = renderParentIntoDocument();
228 + var enterID = ''; // From the window or outside of the React sandbox.
229 + var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
230 + var expectedAggregation = [
231 + {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
232 + {id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
233 + {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
234 + ];
235 + ReactInstanceHandles.traverseEnterLeave(
236 + leaveID, enterID, argAggregator, ARG, ARG2
237 + );
238 + expect(aggregatedArgs).toEqual(expectedAggregation);
239 + });
240 });
241
226 - it("should leave to the window from the shallowest", function() {
227 - var parent = renderParentIntoDocument();
228 - var enterID = ''; // From the window or outside of the React sandbox.
229 - var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
230 - var expectedAggregation = [
231 - {id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
232 - {id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
233 - {id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
234 - ];
235 - traverseEnterLeave(leaveID, enterID, argAggregator, ARG, ARG2);
236 - expect(aggregatedArgs).toEqual(expectedAggregation);
237 - });
242 + describe('getNextDescendantID', function() {
243 + it("should return next descendent from window", function() {
244 + var parent = renderParentIntoDocument();
245 + expect(
246 + ReactInstanceHandles._getNextDescendantID(
247 + '',
248 + parent.refs.P_P1._rootNodeID
249 + )
250 + ).toBe(parent.refs.P._rootNodeID);
251 + });
252
239 - it("should determine the first common ancestor correctly", function() {
240 - var parent = renderParentIntoDocument();
241 - var ancestors = [
242 - // Common ancestor from window to deep element is ''.
243 - { one: {_rootNodeID: ''},
244 - two: parent.refs.P_P1_C1.refs.DIV_1,
245 - com: {_rootNodeID: ''}
246 - },
247 - // Same as previous - reversed direction.
248 - { one: parent.refs.P_P1_C1.refs.DIV_1,
249 - two: {_rootNodeID: ''},
250 - com: {_rootNodeID: ''}
251 - },
252 - // Common ancestor from window to shallow id is ''.
253 - { one: parent.refs.P,
254 - two: {_rootNodeID: ''},
255 - com: {_rootNodeID: ''}
256 - },
257 - // Common ancestor with self is self.
258 - { one: parent.refs.P_P1_C1.refs.DIV_1,
259 - two: parent.refs.P_P1_C1.refs.DIV_1,
260 - com: parent.refs.P_P1_C1.refs.DIV_1
261 - },
262 - // Common ancestor with self is self - even if topmost DOM.
263 - { one: parent.refs.P, two: parent.refs.P, com: parent.refs.P },
264 - // Siblings
265 - {
266 - one: parent.refs.P_P1_C1.refs.DIV_1,
267 - two: parent.refs.P_P1_C1.refs.DIV_2,
268 - com: parent.refs.P_P1_C1.refs.DIV
269 - },
270 - // Common ancestor with parent is the parent.
271 - {
272 - one: parent.refs.P_P1_C1.refs.DIV_1,
273 - two: parent.refs.P_P1_C1.refs.DIV,
274 - com: parent.refs.P_P1_C1.refs.DIV
275 - },
276 - // Common ancestor with grandparent is the grandparent.
277 - {
278 - one: parent.refs.P_P1_C1.refs.DIV_1,
279 - two: parent.refs.P_P1_C1,
280 - com: parent.refs.P_P1_C1
281 - },
282 - // Grantparent across subcomponent boundaries.
283 - {
284 - one: parent.refs.P_P1_C1.refs.DIV_1,
285 - two: parent.refs.P_P1_C2.refs.DIV_1,
286 - com: parent.refs.P_P1
287 - },
288 - // Something deep with something one-off.
289 - {
290 - one: parent.refs.P_P1_C1.refs.DIV_1,
291 - two: parent.refs.P_OneOff,
292 - com: parent.refs.P
293 - }
294 - ];
295 - var i;
296 - for (i = 0; i < ancestors.length; i++) {
297 - var plan = ancestors[i];
298 - var firstCommon = getFirstCommonAncestorID(
299 - plan.one._rootNodeID,
300 - plan.two._rootNodeID
301 - );
302 - expect(firstCommon).toBe(plan.com._rootNodeID);
303 - }
304 - });
305 -});
253 + it("should return window for next descendent towards window", function() {
254 + expect(ReactInstanceHandles._getNextDescendantID('', '')).toBe('');
255 + });
256
307 -describe('ReactInstanceHandles.getReactRootIDFromNodeID', function() {
308 - it('should support strings', function() {
309 - var test = '.reactRoot[s_0_1][0]..[1]';
310 - var expected = '.reactRoot[s_0_1]';
311 - var actual = ReactInstanceHandles.getReactRootIDFromNodeID(test);
312 - expect(actual).toEqual(expected);
257 + it("should return self for next descendent towards self", function() {
258 + var parent = renderParentIntoDocument();
259 + expect(
260 + ReactInstanceHandles._getNextDescendantID(
261 + parent.refs.P_P1._rootNodeID,
262 + parent.refs.P_P1._rootNodeID
263 + )
264 + ).toBe(parent.refs.P_P1._rootNodeID);
265 + });
266 });
314 -});
267
316 -describe('ReactInstanceHandles.isRenderedByReact', function() {
317 - it('should not crash on text nodes', function() {
318 - expect(function() {
319 - ReactInstanceHandles.isRenderedByReact(document.createTextNode('yolo'))
320 - }).not.toThrow();
268 + describe('getFirstCommonAncestorID', function() {
269 + it("should determine the first common ancestor correctly", function() {
270 + var parent = renderParentIntoDocument();
271 + var ancestors = [
272 + // Common ancestor from window to deep element is ''.
273 + { one: {_rootNodeID: ''},
274 + two: parent.refs.P_P1_C1.refs.DIV_1,
275 + com: {_rootNodeID: ''}
276 + },
277 + // Same as previous - reversed direction.
278 + { one: parent.refs.P_P1_C1.refs.DIV_1,
279 + two: {_rootNodeID: ''},
280 + com: {_rootNodeID: ''}
281 + },
282 + // Common ancestor from window to shallow id is ''.
283 + { one: parent.refs.P,
284 + two: {_rootNodeID: ''},
285 + com: {_rootNodeID: ''}
286 + },
287 + // Common ancestor with self is self.
288 + { one: parent.refs.P_P1_C1.refs.DIV_1,
289 + two: parent.refs.P_P1_C1.refs.DIV_1,
290 + com: parent.refs.P_P1_C1.refs.DIV_1
291 + },
292 + // Common ancestor with self is self - even if topmost DOM.
293 + { one: parent.refs.P, two: parent.refs.P, com: parent.refs.P },
294 + // Siblings
295 + {
296 + one: parent.refs.P_P1_C1.refs.DIV_1,
297 + two: parent.refs.P_P1_C1.refs.DIV_2,
298 + com: parent.refs.P_P1_C1.refs.DIV
299 + },
300 + // Common ancestor with parent is the parent.
301 + {
302 + one: parent.refs.P_P1_C1.refs.DIV_1,
303 + two: parent.refs.P_P1_C1.refs.DIV,
304 + com: parent.refs.P_P1_C1.refs.DIV
305 + },
306 + // Common ancestor with grandparent is the grandparent.
307 + {
308 + one: parent.refs.P_P1_C1.refs.DIV_1,
309 + two: parent.refs.P_P1_C1,
310 + com: parent.refs.P_P1_C1
311 + },
312 + // Grantparent across subcomponent boundaries.
313 + {
314 + one: parent.refs.P_P1_C1.refs.DIV_1,
315 + two: parent.refs.P_P1_C2.refs.DIV_1,
316 + com: parent.refs.P_P1
317 + },
318 + // Something deep with something one-off.
319 + {
320 + one: parent.refs.P_P1_C1.refs.DIV_1,
321 + two: parent.refs.P_OneOff,
322 + com: parent.refs.P
323 + }
324 + ];
325 + var i;
326 + for (i = 0; i < ancestors.length; i++) {
327 + var plan = ancestors[i];
328 + var firstCommon = ReactInstanceHandles._getFirstCommonAncestorID(
329 + plan.one._rootNodeID,
330 + plan.two._rootNodeID
331 + );
332 + expect(firstCommon).toBe(plan.com._rootNodeID);
333 + }
334 + });
335 });
336 +
337 });