Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81155 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @emails react-core
10
*/
11
12
"use strict";
13
14
var React = require('React');
15
var ReactTestUtils = require('ReactTestUtils');
16
var ReactMount = require('ReactMount');
17
18
/**
19
* Ensure that all callbacks are invoked, passing this unique argument.
20
*/
21
var ARG = {arg: true};
22
var ARG2 = {arg2: true};
23
24
var ChildComponent = React.createClass({
25
render: function() {
26
return (
27
<div ref="DIV">
28
<div ref="DIV_1" />
29
<div ref="DIV_2" />
30
</div>
31
);
32
}
33
});
34
35
var ParentComponent = React.createClass({
36
render: function() {
37
return (
38
<div ref="P">
39
<div ref="P_P1">
40
<ChildComponent ref="P_P1_C1" />
41
<ChildComponent ref="P_P1_C2" />
42
</div>
43
<div ref="P_OneOff" />
44
</div>
45
);
46
}
47
});
48
49
function renderParentIntoDocument() {
50
return ReactTestUtils.renderIntoDocument(<ParentComponent />);
51
}
52
53
describe('ReactInstanceHandles', function() {
54
var ReactInstanceHandles;
55
56
var aggregatedArgs;
57
function argAggregator(id, isUp, arg) {
58
aggregatedArgs.push({
59
id: id,
60
isUp: isUp,
61
arg: arg
62
});
63
}
64
65
beforeEach(function() {
66
ReactInstanceHandles = require('ReactInstanceHandles');
67
aggregatedArgs = [];
68
});
69
70
describe('isRenderedByReact', function() {
71
it('should not crash on text nodes', function() {
72
expect(function() {
73
ReactMount.isRenderedByReact(document.createTextNode('yolo'));
74
}).not.toThrow();
75
});
76
});
77
78
describe('findComponentRoot', function() {
79
it('should find the correct node with prefix sibling IDs', function() {
80
var parentNode = document.createElement('div');
81
var childNodeA = document.createElement('div');
82
var childNodeB = document.createElement('div');
83
parentNode.appendChild(childNodeA);
84
parentNode.appendChild(childNodeB);
85
86
ReactMount.setID(parentNode, '.0');
87
ReactMount.setID(childNodeA, '.0.0');
88
ReactMount.setID(childNodeB, '.0.0:1');
89
90
expect(
91
ReactMount.findComponentRoot(
92
parentNode,
93
ReactMount.getID(childNodeB)
94
)
95
).toBe(childNodeB);
96
});
97
98
it('should work around unidentified nodes', function() {
99
var parentNode = document.createElement('div');
100
var childNodeA = document.createElement('div');
101
var childNodeB = document.createElement('div');
102
parentNode.appendChild(childNodeA);
103
parentNode.appendChild(childNodeB);
104
105
ReactMount.setID(parentNode, '.0');
106
// No ID on `childNodeA`.
107
ReactMount.setID(childNodeB, '.0.0:1');
108
109
expect(
110
ReactMount.findComponentRoot(
111
parentNode,
112
ReactMount.getID(childNodeB)
113
)
114
).toBe(childNodeB);
115
});
116
117
it('should throw if a rendered element cannot be found', function() {
118
var parentNode = document.createElement('table');
119
var childNodeA = document.createElement('tbody');
120
var childNodeB = document.createElement('tr');
121
parentNode.appendChild(childNodeA);
122
childNodeA.appendChild(childNodeB);
123
124
ReactMount.setID(parentNode, '.0');
125
// No ID on `childNodeA`, it was "rendered by the browser".
126
ReactMount.setID(childNodeB, '.0.1:0');
127
128
expect(ReactMount.findComponentRoot(
129
parentNode,
130
ReactMount.getID(childNodeB)
131
)).toBe(childNodeB);
132
133
expect(function() {
134
ReactMount.findComponentRoot(
135
parentNode,
136
ReactMount.getID(childNodeB) + ":junk"
137
);
138
}).toThrow(
139
'Invariant Violation: findComponentRoot(..., .0.1:0:junk): ' +
140
'Unable to find element. This probably means the DOM was ' +
141
'unexpectedly mutated (e.g., by the browser), usually due to ' +
142
'forgetting a <tbody> when using tables, nesting tags ' +
143
'like <form>, <p>, or <a>, or using non-SVG elements in an <svg> ' +
144
'parent. ' +
145
'Try inspecting the child nodes of the element with React ID `.0`.'
146
);
147
});
148
});
149
150
describe('getReactRootIDFromNodeID', function() {
151
it('should support strings', function() {
152
var test = '.s_0_1.0..1';
153
var expected = '.s_0_1';
154
var actual = ReactInstanceHandles.getReactRootIDFromNodeID(test);
155
expect(actual).toEqual(expected);
156
});
157
});
158
159
describe('getReactRootIDFromNodeID', function() {
160
it('should return null for invalid IDs', function() {
161
var getReactRootIDFromNodeID = (
162
ReactInstanceHandles.getReactRootIDFromNodeID
163
);
164
165
expect(getReactRootIDFromNodeID(null)).toEqual(null);
166
expect(getReactRootIDFromNodeID('.')).toEqual(null);
167
expect(getReactRootIDFromNodeID('#')).toEqual(null);
168
});
169
});
170
171
describe('traverseTwoPhase', function() {
172
it("should not traverse when traversing outside DOM", function() {
173
var targetID = '';
174
var expectedAggregation = [];
175
ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
176
expect(aggregatedArgs).toEqual(expectedAggregation);
177
});
178
179
it("should traverse two phase across component boundary", function() {
180
var parent = renderParentIntoDocument();
181
var targetID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
182
var expectedAggregation = [
183
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
184
{id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG},
185
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG},
186
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: false, arg: ARG},
187
188
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
189
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
190
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
191
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
192
];
193
ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
194
expect(aggregatedArgs).toEqual(expectedAggregation);
195
});
196
197
it("should traverse two phase at shallowest node", function() {
198
var parent = renderParentIntoDocument();
199
var targetID = parent.refs.P._rootNodeID;
200
var expectedAggregation = [
201
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG},
202
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
203
];
204
ReactInstanceHandles.traverseTwoPhase(targetID, argAggregator, ARG);
205
expect(aggregatedArgs).toEqual(expectedAggregation);
206
});
207
});
208
209
describe('traverseEnterLeave', function() {
210
it("should not traverse when enter/leaving outside DOM", function() {
211
var targetID = '';
212
var expectedAggregation = [];
213
ReactInstanceHandles.traverseEnterLeave(
214
targetID, targetID, argAggregator, ARG, ARG2
215
);
216
expect(aggregatedArgs).toEqual(expectedAggregation);
217
});
218
219
it("should not traverse if enter/leave the same node", function() {
220
var parent = renderParentIntoDocument();
221
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
222
var enterID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
223
var expectedAggregation = [];
224
ReactInstanceHandles.traverseEnterLeave(
225
leaveID, enterID, argAggregator, ARG, ARG2
226
);
227
expect(aggregatedArgs).toEqual(expectedAggregation);
228
});
229
230
it("should traverse enter/leave to sibling - avoids parent", function() {
231
var parent = renderParentIntoDocument();
232
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
233
var enterID = parent.refs.P_P1_C1.refs.DIV_2._rootNodeID;
234
var expectedAggregation = [
235
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG},
236
// enter/leave shouldn't fire antyhing on the parent
237
{id: parent.refs.P_P1_C1.refs.DIV_2._rootNodeID, isUp: false, arg: ARG2}
238
];
239
ReactInstanceHandles.traverseEnterLeave(
240
leaveID, enterID, argAggregator, ARG, ARG2
241
);
242
expect(aggregatedArgs).toEqual(expectedAggregation);
243
});
244
245
it("should traverse enter/leave to parent - avoids parent", function() {
246
var parent = renderParentIntoDocument();
247
var leaveID = parent.refs.P_P1_C1.refs.DIV_1._rootNodeID;
248
var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
249
var expectedAggregation = [
250
{id: parent.refs.P_P1_C1.refs.DIV_1._rootNodeID, isUp: true, arg: ARG}
251
];
252
ReactInstanceHandles.traverseEnterLeave(
253
leaveID, enterID, argAggregator, ARG, ARG2
254
);
255
expect(aggregatedArgs).toEqual(expectedAggregation);
256
});
257
258
it("should enter from the window", function() {
259
var parent = renderParentIntoDocument();
260
var leaveID = ''; // From the window or outside of the React sandbox.
261
var enterID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
262
var expectedAggregation = [
263
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2},
264
{id: parent.refs.P_P1._rootNodeID, isUp: false, arg: ARG2},
265
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: false, arg: ARG2}
266
];
267
ReactInstanceHandles.traverseEnterLeave(
268
leaveID, enterID, argAggregator, ARG, ARG2
269
);
270
expect(aggregatedArgs).toEqual(expectedAggregation);
271
});
272
273
it("should enter from the window to the shallowest", function() {
274
var parent = renderParentIntoDocument();
275
var leaveID = ''; // From the window or outside of the React sandbox.
276
var enterID = parent.refs.P._rootNodeID;
277
var expectedAggregation = [
278
{id: parent.refs.P._rootNodeID, isUp: false, arg: ARG2}
279
];
280
ReactInstanceHandles.traverseEnterLeave(
281
leaveID, enterID, argAggregator, ARG, ARG2
282
);
283
expect(aggregatedArgs).toEqual(expectedAggregation);
284
});
285
286
it("should leave to the window", function() {
287
var parent = renderParentIntoDocument();
288
var enterID = ''; // From the window or outside of the React sandbox.
289
var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
290
var expectedAggregation = [
291
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
292
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
293
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
294
];
295
ReactInstanceHandles.traverseEnterLeave(
296
leaveID, enterID, argAggregator, ARG, ARG2
297
);
298
expect(aggregatedArgs).toEqual(expectedAggregation);
299
});
300
301
it("should leave to the window from the shallowest", function() {
302
var parent = renderParentIntoDocument();
303
var enterID = ''; // From the window or outside of the React sandbox.
304
var leaveID = parent.refs.P_P1_C1.refs.DIV._rootNodeID;
305
var expectedAggregation = [
306
{id: parent.refs.P_P1_C1.refs.DIV._rootNodeID, isUp: true, arg: ARG},
307
{id: parent.refs.P_P1._rootNodeID, isUp: true, arg: ARG},
308
{id: parent.refs.P._rootNodeID, isUp: true, arg: ARG}
309
];
310
ReactInstanceHandles.traverseEnterLeave(
311
leaveID, enterID, argAggregator, ARG, ARG2
312
);
313
expect(aggregatedArgs).toEqual(expectedAggregation);
314
});
315
});
316
317
describe('getNextDescendantID', function() {
318
it("should return next descendent from window", function() {
319
var parent = renderParentIntoDocument();
320
expect(
321
ReactInstanceHandles._getNextDescendantID(
322
'',
323
parent.refs.P_P1._rootNodeID
324
)
325
).toBe(parent.refs.P._rootNodeID);
326
});
327
328
it("should return window for next descendent towards window", function() {
329
expect(ReactInstanceHandles._getNextDescendantID('', '')).toBe('');
330
});
331
332
it("should return self for next descendent towards self", function() {
333
var parent = renderParentIntoDocument();
334
expect(
335
ReactInstanceHandles._getNextDescendantID(
336
parent.refs.P_P1._rootNodeID,
337
parent.refs.P_P1._rootNodeID
338
)
339
).toBe(parent.refs.P_P1._rootNodeID);
340
});
341
});
342
343
describe('getFirstCommonAncestorID', function() {
344
it("should determine the first common ancestor correctly", function() {
345
var parent = renderParentIntoDocument();
346
var ancestors = [
347
// Common ancestor from window to deep element is ''.
348
{ one: {_rootNodeID: ''},
349
two: parent.refs.P_P1_C1.refs.DIV_1,
350
com: {_rootNodeID: ''}
351
},
352
// Same as previous - reversed direction.
353
{ one: parent.refs.P_P1_C1.refs.DIV_1,
354
two: {_rootNodeID: ''},
355
com: {_rootNodeID: ''}
356
},
357
// Common ancestor from window to shallow id is ''.
358
{ one: parent.refs.P,
359
two: {_rootNodeID: ''},
360
com: {_rootNodeID: ''}
361
},
362
// Common ancestor with self is self.
363
{ one: parent.refs.P_P1_C1.refs.DIV_1,
364
two: parent.refs.P_P1_C1.refs.DIV_1,
365
com: parent.refs.P_P1_C1.refs.DIV_1
366
},
367
// Common ancestor with self is self - even if topmost DOM.
368
{ one: parent.refs.P, two: parent.refs.P, com: parent.refs.P },
369
// Siblings
370
{
371
one: parent.refs.P_P1_C1.refs.DIV_1,
372
two: parent.refs.P_P1_C1.refs.DIV_2,
373
com: parent.refs.P_P1_C1.refs.DIV
374
},
375
// Common ancestor with parent is the parent.
376
{
377
one: parent.refs.P_P1_C1.refs.DIV_1,
378
two: parent.refs.P_P1_C1.refs.DIV,
379
com: parent.refs.P_P1_C1.refs.DIV
380
},
381
// Common ancestor with grandparent is the grandparent.
382
{
383
one: parent.refs.P_P1_C1.refs.DIV_1,
384
two: parent.refs.P_P1_C1,
385
com: parent.refs.P_P1_C1
386
},
387
// Grantparent across subcomponent boundaries.
388
{
389
one: parent.refs.P_P1_C1.refs.DIV_1,
390
two: parent.refs.P_P1_C2.refs.DIV_1,
391
com: parent.refs.P_P1
392
},
393
// Something deep with something one-off.
394
{
395
one: parent.refs.P_P1_C1.refs.DIV_1,
396
two: parent.refs.P_OneOff,
397
com: parent.refs.P
398
}
399
];
400
var i;
401
for (i = 0; i < ancestors.length; i++) {
402
var plan = ancestors[i];
403
var firstCommon = ReactInstanceHandles._getFirstCommonAncestorID(
404
plan.one._rootNodeID,
405
plan.two._rootNodeID
406
);
407
expect(firstCommon).toBe(plan.com._rootNodeID);
408
}
409
});
410
});
411
412
});
413
414