@samitouri / QOS-React-2 / commits / 57c516cb0e

Don't use magic browser event dispatching for simulated events

When calling ReactTestUtils.Simulate, sometimes you want to test that an exception is thrown by the event handler. This lets you do that without relying on the fact that old jsdom doesn't implement `Event`.

Ben Alpert committed Sep 22, 2015 at 15:29 UTC 57c516cb0e798443b69bab52cd6a3e72007cf5f9
7 files changed +64 -29
src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js
+1 -1
@@ -96,7 +96,7 @@ function manualDispatchChangeEvent(nativeEvent) {
96
97 function runEventInBatch(event) {
98 EventPluginHub.enqueueEvents(event);
99 - EventPluginHub.processEventQueue();
99 + EventPluginHub.processEventQueue(false);
100 }
101
102 function startWatchingForChangeEventIE8(target, targetID) {
src/renderers/shared/event/EventPluginHub.js
+21 -4
@@ -35,17 +35,24 @@ var eventQueue = null;
35 * Dispatches an event and releases it back into the pool, unless persistent.
36 *
37 * @param {?object} event Synthetic event to be dispatched.
38 + * @param {boolean} simulated If the event is simulated (changes exn behavior)
39 * @private
40 */
40 -var executeDispatchesAndRelease = function(event) {
41 +var executeDispatchesAndRelease = function(event, simulated) {
42 if (event) {
42 - EventPluginUtils.executeDispatchesInOrder(event);
43 + EventPluginUtils.executeDispatchesInOrder(event, simulated);
44
45 if (!event.isPersistent()) {
46 event.constructor.release(event);
47 }
48 }
49 };
50 +var executeDispatchesAndReleaseSimulated = function(e) {
51 + return executeDispatchesAndRelease(e, true);
52 +};
53 +var executeDispatchesAndReleaseTopLevel = function(e) {
54 + return executeDispatchesAndRelease(e, false);
55 +};
56
57 /**
58 * - `InstanceHandle`: [required] Module that performs logical traversals of DOM
@@ -266,12 +273,22 @@ var EventPluginHub = {
273 *
274 * @internal
275 */
269 - processEventQueue: function() {
276 + processEventQueue: function(simulated) {
277 // Set `eventQueue` to null before processing it so that we can tell if more
278 // events get enqueued while processing.
279 var processingEventQueue = eventQueue;
280 eventQueue = null;
274 - forEachAccumulated(processingEventQueue, executeDispatchesAndRelease);
281 + if (simulated) {
282 + forEachAccumulated(
283 + processingEventQueue,
284 + executeDispatchesAndReleaseSimulated
285 + );
286 + } else {
287 + forEachAccumulated(
288 + processingEventQueue,
289 + executeDispatchesAndReleaseTopLevel
290 + );
291 + }
292 invariant(
293 !eventQueue,
294 'processEventQueue(): Additional events were enqueued while processing ' +
src/renderers/shared/event/EventPluginUtils.js
+15 -5
@@ -80,20 +80,30 @@ if (__DEV__) {
80 /**
81 * Dispatch the event to the listener.
82 * @param {SyntheticEvent} event SyntheticEvent to handle
83 + * @param {boolean} simulated If the event is simulated (changes exn behavior)
84 * @param {function} listener Application-level callback
85 * @param {string} domID DOM id to pass to the callback.
86 */
86 -function executeDispatch(event, listener, domID) {
87 +function executeDispatch(event, simulated, listener, domID) {
88 var type = event.type || 'unknown-event';
89 event.currentTarget = injection.Mount.getNode(domID);
89 - ReactErrorUtils.invokeGuardedCallback(type, listener, event, domID);
90 + if (simulated) {
91 + ReactErrorUtils.invokeGuardedCallbackWithCatch(
92 + type,
93 + listener,
94 + event,
95 + domID
96 + );
97 + } else {
98 + ReactErrorUtils.invokeGuardedCallback(type, listener, event, domID);
99 + }
100 event.currentTarget = null;
101 }
102
103 /**
104 * Standard/simple iteration through an event's collected dispatches.
105 */
96 -function executeDispatchesInOrder(event) {
106 +function executeDispatchesInOrder(event, simulated) {
107 var dispatchListeners = event._dispatchListeners;
108 var dispatchIDs = event._dispatchIDs;
109 if (__DEV__) {
@@ -105,10 +115,10 @@ function executeDispatchesInOrder(event) {
115 break;
116 }
117 // Listeners and IDs are two parallel arrays that are always in sync.
108 - executeDispatch(event, dispatchListeners[i], dispatchIDs[i]);
118 + executeDispatch(event, simulated, dispatchListeners[i], dispatchIDs[i]);
119 }
120 } else if (dispatchListeners) {
111 - executeDispatch(event, dispatchListeners, dispatchIDs);
121 + executeDispatch(event, simulated, dispatchListeners, dispatchIDs);
122 }
123 event._dispatchListeners = null;
124 event._dispatchIDs = null;
src/renderers/shared/event/eventPlugins/__tests__/ResponderEventPlugin-test.js
+1 -1
@@ -306,7 +306,7 @@ var run = function(config, hierarchyConfig, nativeEventConfig) {
306 // extraction process, but not the side effectful events. Below, we dispatch
307 // side effectful events.
308 EventPluginHub.enqueueEvents(extractedEvents);
309 - EventPluginHub.processEventQueue();
309 + EventPluginHub.processEventQueue(true);
310
311 // Ensure that every event that declared an `order`, was actually dispatched.
312 expect(
src/renderers/shared/reconciler/ReactEventEmitterMixin.js
+1 -1
@@ -15,7 +15,7 @@ var EventPluginHub = require('EventPluginHub');
15
16 function runEventQueueInBatch(events) {
17 EventPluginHub.enqueueEvents(events);
18 - EventPluginHub.processEventQueue();
18 + EventPluginHub.processEventQueue(false);
19 }
20
21 var ReactEventEmitterMixin = {
src/shared/utils/ReactErrorUtils.js
+24 -16
@@ -14,25 +14,33 @@
14
15 var caughtError = null;
16
17 +/**
18 + * Call a function while guarding against errors that happens within it.
19 + *
20 + * @param {?String} name of the guard to use for logging or debugging
21 + * @param {Function} func The function to invoke
22 + * @param {*} a First argument
23 + * @param {*} b Second argument
24 + */
25 +function invokeGuardedCallback(name, func, a, b) {
26 + try {
27 + return func(a, b);
28 + } catch (x) {
29 + if (caughtError === null) {
30 + caughtError = x;
31 + }
32 + return undefined;
33 + }
34 +}
35 +
36 var ReactErrorUtils = {
37 + invokeGuardedCallback: invokeGuardedCallback,
38 +
39 /**
19 - * Call a function while guarding against errors that happens within it.
20 - *
21 - * @param {?String} name of the guard to use for logging or debugging
22 - * @param {Function} func The function to invoke
23 - * @param {*} a First argument
24 - * @param {*} b Second argument
40 + * Invoked by ReactTestUtils.Simulate so that any errors thrown by the event
41 + * handler are sure to be rethrown by rethrowCaughtError.
42 */
26 - invokeGuardedCallback: function(name, func, a, b) {
27 - try {
28 - return func(a, b);
29 - } catch (x) {
30 - if (caughtError === null) {
31 - caughtError = x;
32 - }
33 - return undefined;
34 - }
35 - },
43 + invokeGuardedCallbackWithCatch: invokeGuardedCallback,
44
45 /**
46 * During execution of guarded functions we will capture the first error which
src/test/ReactTestUtils.js
+1 -1
@@ -475,7 +475,7 @@ function makeSimulator(eventType) {
475
476 ReactUpdates.batchedUpdates(function() {
477 EventPluginHub.enqueueEvents(event);
478 - EventPluginHub.processEventQueue();
478 + EventPluginHub.processEventQueue(true);
479 });
480 };
481 }