Simulate synthetic events using ReactTestUtils
Most of the time this is what you want to do, so I've renamed what was Simulate to be SimulateNative. Now Simulate.change does what you expect, so this fixes #517 and fixes #519.
Ben Alpert committed
Jan 18, 2014 at 16:44 UTC
36e97bac21642f73c15d45176dbd304fff327c5a
9 files changed
+120
-43
src/browser/ReactEventEmitter.js
+2
@@ -318,6 +318,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
318
}
319
},
320
321
+ eventNameDispatchConfigs: EventPluginHub.eventNameDispatchConfigs,
322
+
323
registrationNameModules: EventPluginHub.registrationNameModules,
324
325
putListener: EventPluginHub.putListener,
src/browser/__tests__/ReactEventEmitter-test.js
+10
-10
@@ -142,10 +142,10 @@ describe('ReactEventEmitter', function() {
142
it('should not invoke handlers if ReactEventEmitter is disabled', function() {
143
registerSimpleTestHandler();
144
ReactEventEmitter.setEnabled(false);
145
- ReactTestUtils.Simulate.click(CHILD);
145
+ ReactTestUtils.SimulateNative.click(CHILD);
146
expect(LISTENER.mock.calls.length).toBe(0);
147
ReactEventEmitter.setEnabled(true);
148
- ReactTestUtils.Simulate.click(CHILD);
148
+ ReactTestUtils.SimulateNative.click(CHILD);
149
expect(LISTENER.mock.calls.length).toBe(1);
150
});
151
@@ -312,11 +312,11 @@ describe('ReactEventEmitter', function() {
312
ON_TOUCH_TAP_KEY,
313
recordID.bind(null, getID(CHILD))
314
);
315
- ReactTestUtils.Simulate.touchStart(
315
+ ReactTestUtils.SimulateNative.touchStart(
316
CHILD,
317
ReactTestUtils.nativeTouchData(0, 0)
318
);
319
- ReactTestUtils.Simulate.touchEnd(
319
+ ReactTestUtils.SimulateNative.touchEnd(
320
CHILD,
321
ReactTestUtils.nativeTouchData(0, 0)
322
);
@@ -330,11 +330,11 @@ describe('ReactEventEmitter', function() {
330
ON_TOUCH_TAP_KEY,
331
recordID.bind(null, getID(CHILD))
332
);
333
- ReactTestUtils.Simulate.touchStart(
333
+ ReactTestUtils.SimulateNative.touchStart(
334
CHILD,
335
ReactTestUtils.nativeTouchData(0, 0)
336
);
337
- ReactTestUtils.Simulate.touchEnd(
337
+ ReactTestUtils.SimulateNative.touchEnd(
338
CHILD,
339
ReactTestUtils.nativeTouchData(0, tapMoveThreshold - 1)
340
);
@@ -348,11 +348,11 @@ describe('ReactEventEmitter', function() {
348
ON_TOUCH_TAP_KEY,
349
recordID.bind(null, getID(CHILD))
350
);
351
- ReactTestUtils.Simulate.touchStart(
351
+ ReactTestUtils.SimulateNative.touchStart(
352
CHILD,
353
ReactTestUtils.nativeTouchData(0, 0)
354
);
355
- ReactTestUtils.Simulate.touchEnd(
355
+ ReactTestUtils.SimulateNative.touchEnd(
356
CHILD,
357
ReactTestUtils.nativeTouchData(0, tapMoveThreshold + 1)
358
);
@@ -415,11 +415,11 @@ describe('ReactEventEmitter', function() {
415
ON_TOUCH_TAP_KEY,
416
recordID.bind(null, getID(GRANDPARENT))
417
);
418
- ReactTestUtils.Simulate.touchStart(
418
+ ReactTestUtils.SimulateNative.touchStart(
419
CHILD,
420
ReactTestUtils.nativeTouchData(0, 0)
421
);
422
- ReactTestUtils.Simulate.touchEnd(
422
+ ReactTestUtils.SimulateNative.touchEnd(
423
CHILD,
424
ReactTestUtils.nativeTouchData(0, 0)
425
);
src/browser/dom/components/__tests__/ReactDOMInput-test.js
+5
-6
@@ -125,7 +125,7 @@ describe('ReactDOMInput', function() {
125
var node = renderTextInput(stub);
126
127
node.value = 'giraffe';
128
- ReactTestUtils.Simulate.input(node);
128
+ ReactTestUtils.Simulate.change(node);
129
expect(node.value).toBe('0');
130
});
131
@@ -171,9 +171,8 @@ describe('ReactDOMInput', function() {
171
aNode.checked = false;
172
expect(cNode.checked).toBe(true);
173
174
- // Now let's run the actual ReactDOMInput change event handler (on radio
175
- // inputs, ChangeEventPlugin listens for the `click` event so trigger that)
176
- ReactTestUtils.Simulate.click(bNode);
174
+ // Now let's run the actual ReactDOMInput change event handler
175
+ ReactTestUtils.Simulate.change(bNode);
176
177
// The original state should have been restored
178
expect(aNode.checked).toBe(true);
@@ -192,7 +191,7 @@ describe('ReactDOMInput', function() {
191
expect(link.requestChange.mock.calls.length).toBe(0);
192
193
instance.getDOMNode().value = 'test';
195
- ReactTestUtils.Simulate.input(instance.getDOMNode());
194
+ ReactTestUtils.Simulate.change(instance.getDOMNode());
195
196
expect(link.requestChange.mock.calls.length).toBe(1);
197
expect(link.requestChange.mock.calls[0][0]).toEqual('test');
@@ -265,7 +264,7 @@ describe('ReactDOMInput', function() {
264
expect(link.requestChange.mock.calls.length).toBe(0);
265
266
instance.getDOMNode().checked = false;
268
- ReactTestUtils.Simulate.click(instance.getDOMNode());
267
+ ReactTestUtils.Simulate.change(instance.getDOMNode());
268
269
expect(link.requestChange.mock.calls.length).toBe(1);
270
expect(link.requestChange.mock.calls[0][0]).toEqual(false);
src/browser/dom/components/__tests__/ReactDOMTextarea-test.js
+2
-2
@@ -139,7 +139,7 @@ describe('ReactDOMTextarea', function() {
139
var node = renderTextarea(stub);
140
141
node.value = 'giraffe';
142
- ReactTestUtils.Simulate.input(node);
142
+ ReactTestUtils.Simulate.change(node);
143
expect(node.value).toBe('0');
144
});
145
@@ -216,7 +216,7 @@ describe('ReactDOMTextarea', function() {
216
expect(link.requestChange.mock.calls.length).toBe(0);
217
218
instance.getDOMNode().value = 'test';
219
- ReactTestUtils.Simulate.input(instance.getDOMNode());
219
+ ReactTestUtils.Simulate.change(instance.getDOMNode());
220
221
expect(link.requestChange.mock.calls.length).toBe(1);
222
expect(link.requestChange.mock.calls[0][0]).toEqual('test');
src/browser/eventPlugins/__tests__/AnalyticsEventPlugin-test.js
+4
-4
@@ -99,14 +99,14 @@ describe('AnalyticsEventPlugin', function() {
99
100
// Simulate some clicks
101
for (var i = 0; i < numClickEvents; i++) {
102
- ReactTestUtils.Simulate.click(renderedComponent.refs.testDiv);
102
+ ReactTestUtils.SimulateNative.click(renderedComponent.refs.testDiv);
103
}
104
// Simulate some double clicks
105
for (i = 0; i < numDoubleClickEvents; i++) {
106
- ReactTestUtils.Simulate.doubleClick(renderedComponent.refs.testDiv);
106
+ ReactTestUtils.SimulateNative.doubleClick(renderedComponent.refs.testDiv);
107
}
108
// Simulate some other events not being tracked for analytics
109
- ReactTestUtils.Simulate.focus(renderedComponent.refs.testDiv);
109
+ ReactTestUtils.SimulateNative.focus(renderedComponent.refs.testDiv);
110
111
window.mockRunTimersOnce();
112
expect(cb).toBeCalled();
@@ -143,7 +143,7 @@ describe('AnalyticsEventPlugin', function() {
143
144
var error = false;
145
try {
146
- ReactTestUtils.Simulate.click(renderedComponent.refs.testDiv);
146
+ ReactTestUtils.SimulateNative.click(renderedComponent.refs.testDiv);
147
} catch(e) {
148
error = true;
149
}
src/core/__tests__/ReactBind-test.js
+2
-2
@@ -52,8 +52,8 @@ describe('autobinding', function() {
52
render: function() {
53
return (
54
<div
55
- onMouseEnter={this.onMouseEnter.bind(this)}
56
- onMouseLeave={this.onMouseLeave}
55
+ onMouseOver={this.onMouseEnter.bind(this)}
56
+ onMouseOut={this.onMouseLeave}
57
onClick={this.onClick}
58
/>
59
);
src/event/EventPluginHub.js
+2
@@ -141,6 +141,8 @@ var EventPluginHub = {
141
142
},
143
144
+ eventNameDispatchConfigs: EventPluginRegistry.eventNameDispatchConfigs,
145
+
146
registrationNameModules: EventPluginRegistry.registrationNameModules,
147
148
/**
src/event/EventPluginRegistry.js
+22
-1
@@ -85,6 +85,14 @@ function recomputePluginOrdering() {
85
* @private
86
*/
87
function publishEventForPlugin(dispatchConfig, PluginModule, eventName) {
88
+ invariant(
89
+ !EventPluginRegistry.eventNameDispatchConfigs[eventName],
90
+ 'EventPluginHub: More than one plugin attempted to publish the same ' +
91
+ 'event name, `%s`.',
92
+ eventName
93
+ );
94
+ EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig;
95
+
96
var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
97
if (phasedRegistrationNames) {
98
for (var phaseName in phasedRegistrationNames) {
@@ -142,7 +150,12 @@ var EventPluginRegistry = {
150
plugins: [],
151
152
/**
145
- * Mapping from registration names to plugin modules.
153
+ * Mapping from event name to dispatch config
154
+ */
155
+ eventNameDispatchConfigs: {},
156
+
157
+ /**
158
+ * Mapping from registration name to plugin module
159
*/
160
registrationNameModules: {},
161
@@ -243,6 +256,14 @@ var EventPluginRegistry = {
256
}
257
}
258
EventPluginRegistry.plugins.length = 0;
259
+
260
+ var eventNameDispatchConfigs = EventPluginRegistry.eventNameDispatchConfigs;
261
+ for (var eventName in eventNameDispatchConfigs) {
262
+ if (eventNameDispatchConfigs.hasOwnProperty(eventName)) {
263
+ delete eventNameDispatchConfigs[eventName];
264
+ }
265
+ }
266
+
267
var registrationNameModules = EventPluginRegistry.registrationNameModules;
268
for (var registrationName in registrationNameModules) {
269
if (registrationNameModules.hasOwnProperty(registrationName)) {
src/test/ReactTestUtils.js
+71
-18
@@ -16,12 +16,19 @@
16
* @providesModule ReactTestUtils
17
*/
18
19
+"use strict";
20
+
21
var EventConstants = require('EventConstants');
22
+var EventPluginHub = require('EventPluginHub');
23
+var EventPropagators = require('EventPropagators');
24
var React = require('React');
25
var ReactComponent = require('ReactComponent');
26
var ReactDOM = require('ReactDOM');
27
var ReactEventEmitter = require('ReactEventEmitter');
28
+var ReactMount = require('ReactMount');
29
var ReactTextComponent = require('ReactTextComponent');
30
+var ReactUpdates = require('ReactUpdates');
31
+var SyntheticEvent = require('SyntheticEvent');
32
33
var mergeInto = require('mergeInto');
34
var copyProperties = require('copyProperties');
@@ -227,12 +234,12 @@ var ReactTestUtils = {
234
235
/**
236
* Simulates a top level event being dispatched from a raw event that occured
230
- * on and `Element` node.
237
+ * on an `Element` node.
238
* @param topLevelType {Object} A type from `EventConstants.topLevelTypes`
239
* @param {!Element} node The dom to simulate an event occurring on.
240
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
241
*/
235
- simulateEventOnNode: function(topLevelType, node, fakeNativeEvent) {
242
+ simulateNativeEventOnNode: function(topLevelType, node, fakeNativeEvent) {
243
var virtualHandler =
244
ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
245
topLevelType
@@ -248,8 +255,11 @@ var ReactTestUtils = {
255
* @param comp {!ReactDOMComponent}
256
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
257
*/
251
- simulateEventOnDOMComponent: function(topLevelType, comp, fakeNativeEvent) {
252
- ReactTestUtils.simulateEventOnNode(
258
+ simulateNativeEventOnDOMComponent: function(
259
+ topLevelType,
260
+ comp,
261
+ fakeNativeEvent) {
262
+ ReactTestUtils.simulateNativeEventOnNode(
263
topLevelType,
264
comp.getDOMNode(),
265
fakeNativeEvent
@@ -264,7 +274,8 @@ var ReactTestUtils = {
274
};
275
},
276
267
- Simulate: null // Will populate
277
+ Simulate: {},
278
+ SimulateNative: {}
279
};
280
281
/**
@@ -272,33 +283,75 @@ var ReactTestUtils = {
283
*
284
* - `ReactTestUtils.Simulate.click(Element/ReactDOMComponent)`
285
* - `ReactTestUtils.Simulate.mouseMove(Element/ReactDOMComponent)`
275
- * - `ReactTestUtils.Simulate.mouseIn/ReactDOMComponent)`
276
- * - `ReactTestUtils.Simulate.mouseOut(Element/ReactDOMComponent)`
286
+ * - `ReactTestUtils.Simulate.change(Element/ReactDOMComponent)`
287
+ * - ... (All keys from event plugin `eventTypes` objects)
288
+ */
289
+function makeSimulator(eventType) {
290
+ return function(domComponentOrNode, eventData) {
291
+ var node;
292
+ if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
293
+ node = domComponentOrNode.getDOMNode();
294
+ } else if (domComponentOrNode.tagName) {
295
+ node = domComponentOrNode;
296
+ }
297
+
298
+ var fakeNativeEvent = new Event();
299
+ fakeNativeEvent.target = node;
300
+ // We don't use SyntheticEvent.getPooled in order to not have to worry about
301
+ // properly destroying any properties assigned from `eventData` upon release
302
+ var event = new SyntheticEvent(
303
+ ReactEventEmitter.eventNameDispatchConfigs[eventType],
304
+ ReactMount.getID(node),
305
+ fakeNativeEvent
306
+ );
307
+ mergeInto(event, eventData);
308
+ EventPropagators.accumulateTwoPhaseDispatches(event);
309
+
310
+ ReactUpdates.batchedUpdates(function() {
311
+ EventPluginHub.enqueueEvents(event);
312
+ EventPluginHub.processEventQueue();
313
+ });
314
+ };
315
+}
316
+
317
+var eventType;
318
+for (eventType in ReactEventEmitter.eventNameDispatchConfigs) {
319
+ /**
320
+ * @param {!Element || ReactDOMComponent} domComponentOrNode
321
+ * @param {?object} eventData Fake event data to use in SyntheticEvent.
322
+ */
323
+ ReactTestUtils.Simulate[eventType] = makeSimulator(eventType);
324
+}
325
+
326
+/**
327
+ * Exports:
328
+ *
329
+ * - `ReactTestUtils.SimulateNative.click(Element/ReactDOMComponent)`
330
+ * - `ReactTestUtils.SimulateNative.mouseMove(Element/ReactDOMComponent)`
331
+ * - `ReactTestUtils.SimulateNative.mouseIn/ReactDOMComponent)`
332
+ * - `ReactTestUtils.SimulateNative.mouseOut(Element/ReactDOMComponent)`
333
* - ... (All keys from `EventConstants.topLevelTypes`)
334
*
335
* Note: Top level event types are a subset of the entire set of handler types
336
* (which include a broader set of "synthetic" events). For example, onDragDone
281
- * is a synthetic event. You certainly may write test cases for these event
282
- * types, but it doesn't make sense to simulate them at this low of a level. In
283
- * this case, the way you test an `onDragDone` event is by simulating a series
284
- * of `mouseMove`/ `mouseDown`/`mouseUp` events - Then, a synthetic event of
285
- * type `onDragDone` will be constructed and dispached through your system
286
- * automatically.
337
+ * is a synthetic event. Except when testing an event plugin or React's event
338
+ * handling code specifically, you probably want to use ReactTestUtils.Simulate
339
+ * to dispatch synthetic events.
340
*/
341
289
-function makeSimulator(eventType) {
342
+function makeNativeSimulator(eventType) {
343
return function(domComponentOrNode, nativeEventData) {
344
var fakeNativeEvent = new Event(eventType);
345
mergeInto(fakeNativeEvent, nativeEventData);
346
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
294
- ReactTestUtils.simulateEventOnDOMComponent(
347
+ ReactTestUtils.simulateNativeEventOnDOMComponent(
348
eventType,
349
domComponentOrNode,
350
fakeNativeEvent
351
);
352
} else if (!!domComponentOrNode.tagName) {
353
// Will allow on actual dom nodes.
301
- ReactTestUtils.simulateEventOnNode(
354
+ ReactTestUtils.simulateNativeEventOnNode(
355
eventType,
356
domComponentOrNode,
357
fakeNativeEvent
@@ -307,7 +360,6 @@ function makeSimulator(eventType) {
360
};
361
}
362
310
-ReactTestUtils.Simulate = {};
363
var eventType;
364
for (eventType in topLevelTypes) {
365
// Event type is stored as 'topClick' - we transform that to 'click'
@@ -317,7 +369,8 @@ for (eventType in topLevelTypes) {
369
* @param {!Element || ReactDOMComponent} domComponentOrNode
370
* @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
371
*/
320
- ReactTestUtils.Simulate[convenienceName] = makeSimulator(eventType);
372
+ ReactTestUtils.SimulateNative[convenienceName] =
373
+ makeNativeSimulator(eventType);
374
}
375
376
module.exports = ReactTestUtils;