@samitouri / QOS-React-2 / commits / 36e97bac21

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;