Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81152 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
* @providesModule ReactTestUtils
10
*/
11
12
"use strict";
13
14
var EventConstants = require('EventConstants');
15
var EventPluginHub = require('EventPluginHub');
16
var EventPropagators = require('EventPropagators');
17
var React = require('React');
18
var ReactElement = require('ReactElement');
19
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
20
var ReactMount = require('ReactMount');
21
var ReactTextComponent = require('ReactTextComponent');
22
var ReactUpdates = require('ReactUpdates');
23
var SyntheticEvent = require('SyntheticEvent');
24
25
var assign = require('Object.assign');
26
27
var topLevelTypes = EventConstants.topLevelTypes;
28
29
function Event(suffix) {}
30
31
/**
32
* @class ReactTestUtils
33
*/
34
35
/**
36
* Todo: Support the entire DOM.scry query syntax. For now, these simple
37
* utilities will suffice for testing purposes.
38
* @lends ReactTestUtils
39
*/
40
var ReactTestUtils = {
41
renderIntoDocument: function(instance) {
42
var div = document.createElement('div');
43
// None of our tests actually require attaching the container to the
44
// DOM, and doing so creates a mess that we rely on test isolation to
45
// clean up, so we're going to stop honoring the name of this method
46
// (and probably rename it eventually) if no problems arise.
47
// document.documentElement.appendChild(div);
48
return React.render(instance, div);
49
},
50
51
isElement: function(element) {
52
return ReactElement.isValidElement(element);
53
},
54
55
isElementOfType: function(inst, convenienceConstructor) {
56
return (
57
ReactElement.isValidElement(inst) &&
58
inst.type === convenienceConstructor.type
59
);
60
},
61
62
isDOMComponent: function(inst) {
63
return !!(inst && inst.mountComponent && inst.tagName);
64
},
65
66
isDOMComponentElement: function(inst) {
67
return !!(inst &&
68
ReactElement.isValidElement(inst) &&
69
!!inst.tagName);
70
},
71
72
isCompositeComponent: function(inst) {
73
return typeof inst.render === 'function' &&
74
typeof inst.setState === 'function';
75
},
76
77
isCompositeComponentWithType: function(inst, type) {
78
return !!(ReactTestUtils.isCompositeComponent(inst) &&
79
(inst.constructor === type.type));
80
},
81
82
isCompositeComponentElement: function(inst) {
83
if (!ReactElement.isValidElement(inst)) {
84
return false;
85
}
86
// We check the prototype of the type that will get mounted, not the
87
// instance itself. This is a future proof way of duck typing.
88
var prototype = inst.type.prototype;
89
return (
90
typeof prototype.render === 'function' &&
91
typeof prototype.setState === 'function'
92
);
93
},
94
95
isCompositeComponentElementWithType: function(inst, type) {
96
return !!(ReactTestUtils.isCompositeComponentElement(inst) &&
97
(inst.constructor === type));
98
},
99
100
isTextComponent: function(inst) {
101
return inst instanceof ReactTextComponent.type;
102
},
103
104
findAllInRenderedTree: function(inst, test) {
105
if (!inst) {
106
return [];
107
}
108
var ret = test(inst) ? [inst] : [];
109
if (ReactTestUtils.isDOMComponent(inst)) {
110
var renderedChildren = inst._renderedChildren;
111
var key;
112
for (key in renderedChildren) {
113
if (!renderedChildren.hasOwnProperty(key)) {
114
continue;
115
}
116
ret = ret.concat(
117
ReactTestUtils.findAllInRenderedTree(renderedChildren[key], test)
118
);
119
}
120
} else if (ReactTestUtils.isCompositeComponent(inst)) {
121
ret = ret.concat(
122
ReactTestUtils.findAllInRenderedTree(inst._renderedComponent, test)
123
);
124
}
125
return ret;
126
},
127
128
/**
129
* Finds all instance of components in the rendered tree that are DOM
130
* components with the class name matching `className`.
131
* @return an array of all the matches.
132
*/
133
scryRenderedDOMComponentsWithClass: function(root, className) {
134
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
135
var instClassName = inst.props.className;
136
return ReactTestUtils.isDOMComponent(inst) && (
137
instClassName &&
138
(' ' + instClassName + ' ').indexOf(' ' + className + ' ') !== -1
139
);
140
});
141
},
142
143
/**
144
* Like scryRenderedDOMComponentsWithClass but expects there to be one result,
145
* and returns that one result, or throws exception if there is any other
146
* number of matches besides one.
147
* @return {!ReactDOMComponent} The one match.
148
*/
149
findRenderedDOMComponentWithClass: function(root, className) {
150
var all =
151
ReactTestUtils.scryRenderedDOMComponentsWithClass(root, className);
152
if (all.length !== 1) {
153
throw new Error('Did not find exactly one match for class:' + className);
154
}
155
return all[0];
156
},
157
158
159
/**
160
* Finds all instance of components in the rendered tree that are DOM
161
* components with the tag name matching `tagName`.
162
* @return an array of all the matches.
163
*/
164
scryRenderedDOMComponentsWithTag: function(root, tagName) {
165
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
166
return ReactTestUtils.isDOMComponent(inst) &&
167
inst.tagName === tagName.toUpperCase();
168
});
169
},
170
171
/**
172
* Like scryRenderedDOMComponentsWithTag but expects there to be one result,
173
* and returns that one result, or throws exception if there is any other
174
* number of matches besides one.
175
* @return {!ReactDOMComponent} The one match.
176
*/
177
findRenderedDOMComponentWithTag: function(root, tagName) {
178
var all = ReactTestUtils.scryRenderedDOMComponentsWithTag(root, tagName);
179
if (all.length !== 1) {
180
throw new Error('Did not find exactly one match for tag:' + tagName);
181
}
182
return all[0];
183
},
184
185
186
/**
187
* Finds all instances of components with type equal to `componentType`.
188
* @return an array of all the matches.
189
*/
190
scryRenderedComponentsWithType: function(root, componentType) {
191
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
192
return ReactTestUtils.isCompositeComponentWithType(
193
inst,
194
componentType
195
);
196
});
197
},
198
199
/**
200
* Same as `scryRenderedComponentsWithType` but expects there to be one result
201
* and returns that one result, or throws exception if there is any other
202
* number of matches besides one.
203
* @return {!ReactComponent} The one match.
204
*/
205
findRenderedComponentWithType: function(root, componentType) {
206
var all = ReactTestUtils.scryRenderedComponentsWithType(
207
root,
208
componentType
209
);
210
if (all.length !== 1) {
211
throw new Error(
212
'Did not find exactly one match for componentType:' + componentType
213
);
214
}
215
return all[0];
216
},
217
218
/**
219
* Pass a mocked component module to this method to augment it with
220
* useful methods that allow it to be used as a dummy React component.
221
* Instead of rendering as usual, the component will become a simple
222
* <div> containing any provided children.
223
*
224
* @param {object} module the mock function object exported from a
225
* module that defines the component to be mocked
226
* @param {?string} mockTagName optional dummy root tag name to return
227
* from render method (overrides
228
* module.mockTagName if provided)
229
* @return {object} the ReactTestUtils object (for chaining)
230
*/
231
mockComponent: function(module, mockTagName) {
232
mockTagName = mockTagName || module.mockTagName || "div";
233
234
var ConvenienceConstructor = React.createClass({
235
render: function() {
236
return React.createElement(
237
mockTagName,
238
null,
239
this.props.children
240
);
241
}
242
});
243
244
module.mockImplementation(ConvenienceConstructor);
245
246
module.type = ConvenienceConstructor.type;
247
module.isReactLegacyFactory = true;
248
249
return this;
250
},
251
252
/**
253
* Simulates a top level event being dispatched from a raw event that occured
254
* on an `Element` node.
255
* @param topLevelType {Object} A type from `EventConstants.topLevelTypes`
256
* @param {!Element} node The dom to simulate an event occurring on.
257
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
258
*/
259
simulateNativeEventOnNode: function(topLevelType, node, fakeNativeEvent) {
260
fakeNativeEvent.target = node;
261
ReactBrowserEventEmitter.ReactEventListener.dispatchEvent(
262
topLevelType,
263
fakeNativeEvent
264
);
265
},
266
267
/**
268
* Simulates a top level event being dispatched from a raw event that occured
269
* on the `ReactDOMComponent` `comp`.
270
* @param topLevelType {Object} A type from `EventConstants.topLevelTypes`.
271
* @param comp {!ReactDOMComponent}
272
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
273
*/
274
simulateNativeEventOnDOMComponent: function(
275
topLevelType,
276
comp,
277
fakeNativeEvent) {
278
ReactTestUtils.simulateNativeEventOnNode(
279
topLevelType,
280
comp.getDOMNode(),
281
fakeNativeEvent
282
);
283
},
284
285
nativeTouchData: function(x, y) {
286
return {
287
touches: [
288
{pageX: x, pageY: y}
289
]
290
};
291
},
292
293
Simulate: null,
294
SimulateNative: {}
295
};
296
297
/**
298
* Exports:
299
*
300
* - `ReactTestUtils.Simulate.click(Element/ReactDOMComponent)`
301
* - `ReactTestUtils.Simulate.mouseMove(Element/ReactDOMComponent)`
302
* - `ReactTestUtils.Simulate.change(Element/ReactDOMComponent)`
303
* - ... (All keys from event plugin `eventTypes` objects)
304
*/
305
function makeSimulator(eventType) {
306
return function(domComponentOrNode, eventData) {
307
var node;
308
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
309
node = domComponentOrNode.getDOMNode();
310
} else if (domComponentOrNode.tagName) {
311
node = domComponentOrNode;
312
}
313
314
var fakeNativeEvent = new Event();
315
fakeNativeEvent.target = node;
316
// We don't use SyntheticEvent.getPooled in order to not have to worry about
317
// properly destroying any properties assigned from `eventData` upon release
318
var event = new SyntheticEvent(
319
ReactBrowserEventEmitter.eventNameDispatchConfigs[eventType],
320
ReactMount.getID(node),
321
fakeNativeEvent
322
);
323
assign(event, eventData);
324
EventPropagators.accumulateTwoPhaseDispatches(event);
325
326
ReactUpdates.batchedUpdates(function() {
327
EventPluginHub.enqueueEvents(event);
328
EventPluginHub.processEventQueue();
329
});
330
};
331
}
332
333
function buildSimulators() {
334
ReactTestUtils.Simulate = {};
335
336
var eventType;
337
for (eventType in ReactBrowserEventEmitter.eventNameDispatchConfigs) {
338
/**
339
* @param {!Element || ReactDOMComponent} domComponentOrNode
340
* @param {?object} eventData Fake event data to use in SyntheticEvent.
341
*/
342
ReactTestUtils.Simulate[eventType] = makeSimulator(eventType);
343
}
344
}
345
346
// Rebuild ReactTestUtils.Simulate whenever event plugins are injected
347
var oldInjectEventPluginOrder = EventPluginHub.injection.injectEventPluginOrder;
348
EventPluginHub.injection.injectEventPluginOrder = function() {
349
oldInjectEventPluginOrder.apply(this, arguments);
350
buildSimulators();
351
};
352
var oldInjectEventPlugins = EventPluginHub.injection.injectEventPluginsByName;
353
EventPluginHub.injection.injectEventPluginsByName = function() {
354
oldInjectEventPlugins.apply(this, arguments);
355
buildSimulators();
356
};
357
358
buildSimulators();
359
360
/**
361
* Exports:
362
*
363
* - `ReactTestUtils.SimulateNative.click(Element/ReactDOMComponent)`
364
* - `ReactTestUtils.SimulateNative.mouseMove(Element/ReactDOMComponent)`
365
* - `ReactTestUtils.SimulateNative.mouseIn/ReactDOMComponent)`
366
* - `ReactTestUtils.SimulateNative.mouseOut(Element/ReactDOMComponent)`
367
* - ... (All keys from `EventConstants.topLevelTypes`)
368
*
369
* Note: Top level event types are a subset of the entire set of handler types
370
* (which include a broader set of "synthetic" events). For example, onDragDone
371
* is a synthetic event. Except when testing an event plugin or React's event
372
* handling code specifically, you probably want to use ReactTestUtils.Simulate
373
* to dispatch synthetic events.
374
*/
375
376
function makeNativeSimulator(eventType) {
377
return function(domComponentOrNode, nativeEventData) {
378
var fakeNativeEvent = new Event(eventType);
379
assign(fakeNativeEvent, nativeEventData);
380
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
381
ReactTestUtils.simulateNativeEventOnDOMComponent(
382
eventType,
383
domComponentOrNode,
384
fakeNativeEvent
385
);
386
} else if (!!domComponentOrNode.tagName) {
387
// Will allow on actual dom nodes.
388
ReactTestUtils.simulateNativeEventOnNode(
389
eventType,
390
domComponentOrNode,
391
fakeNativeEvent
392
);
393
}
394
};
395
}
396
397
var eventType;
398
for (eventType in topLevelTypes) {
399
// Event type is stored as 'topClick' - we transform that to 'click'
400
var convenienceName = eventType.indexOf('top') === 0 ?
401
eventType.charAt(3).toLowerCase() + eventType.substr(4) : eventType;
402
/**
403
* @param {!Element || ReactDOMComponent} domComponentOrNode
404
* @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
405
*/
406
ReactTestUtils.SimulateNative[convenienceName] =
407
makeNativeSimulator(eventType);
408
}
409
410
module.exports = ReactTestUtils;
411
412