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
}