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`.
This commit is contained in:
Ben Alpert
2015-09-22 15:31:08 -07:00
parent 0f67febad7
commit 57c516cb0e
7 changed files with 66 additions and 31 deletions
@@ -96,7 +96,7 @@ function manualDispatchChangeEvent(nativeEvent) {
function runEventInBatch(event) {
EventPluginHub.enqueueEvents(event);
EventPluginHub.processEventQueue();
EventPluginHub.processEventQueue(false);
}
function startWatchingForChangeEventIE8(target, targetID) {
+21 -4
View File
@@ -35,17 +35,24 @@ var eventQueue = null;
* Dispatches an event and releases it back into the pool, unless persistent.
*
* @param {?object} event Synthetic event to be dispatched.
* @param {boolean} simulated If the event is simulated (changes exn behavior)
* @private
*/
var executeDispatchesAndRelease = function(event) {
var executeDispatchesAndRelease = function(event, simulated) {
if (event) {
EventPluginUtils.executeDispatchesInOrder(event);
EventPluginUtils.executeDispatchesInOrder(event, simulated);
if (!event.isPersistent()) {
event.constructor.release(event);
}
}
};
var executeDispatchesAndReleaseSimulated = function(e) {
return executeDispatchesAndRelease(e, true);
};
var executeDispatchesAndReleaseTopLevel = function(e) {
return executeDispatchesAndRelease(e, false);
};
/**
* - `InstanceHandle`: [required] Module that performs logical traversals of DOM
@@ -266,12 +273,22 @@ var EventPluginHub = {
*
* @internal
*/
processEventQueue: function() {
processEventQueue: function(simulated) {
// Set `eventQueue` to null before processing it so that we can tell if more
// events get enqueued while processing.
var processingEventQueue = eventQueue;
eventQueue = null;
forEachAccumulated(processingEventQueue, executeDispatchesAndRelease);
if (simulated) {
forEachAccumulated(
processingEventQueue,
executeDispatchesAndReleaseSimulated
);
} else {
forEachAccumulated(
processingEventQueue,
executeDispatchesAndReleaseTopLevel
);
}
invariant(
!eventQueue,
'processEventQueue(): Additional events were enqueued while processing ' +
+15 -5
View File
@@ -80,20 +80,30 @@ if (__DEV__) {
/**
* Dispatch the event to the listener.
* @param {SyntheticEvent} event SyntheticEvent to handle
* @param {boolean} simulated If the event is simulated (changes exn behavior)
* @param {function} listener Application-level callback
* @param {string} domID DOM id to pass to the callback.
*/
function executeDispatch(event, listener, domID) {
function executeDispatch(event, simulated, listener, domID) {
var type = event.type || 'unknown-event';
event.currentTarget = injection.Mount.getNode(domID);
ReactErrorUtils.invokeGuardedCallback(type, listener, event, domID);
if (simulated) {
ReactErrorUtils.invokeGuardedCallbackWithCatch(
type,
listener,
event,
domID
);
} else {
ReactErrorUtils.invokeGuardedCallback(type, listener, event, domID);
}
event.currentTarget = null;
}
/**
* Standard/simple iteration through an event's collected dispatches.
*/
function executeDispatchesInOrder(event) {
function executeDispatchesInOrder(event, simulated) {
var dispatchListeners = event._dispatchListeners;
var dispatchIDs = event._dispatchIDs;
if (__DEV__) {
@@ -105,10 +115,10 @@ function executeDispatchesInOrder(event) {
break;
}
// Listeners and IDs are two parallel arrays that are always in sync.
executeDispatch(event, dispatchListeners[i], dispatchIDs[i]);
executeDispatch(event, simulated, dispatchListeners[i], dispatchIDs[i]);
}
} else if (dispatchListeners) {
executeDispatch(event, dispatchListeners, dispatchIDs);
executeDispatch(event, simulated, dispatchListeners, dispatchIDs);
}
event._dispatchListeners = null;
event._dispatchIDs = null;
@@ -306,7 +306,7 @@ var run = function(config, hierarchyConfig, nativeEventConfig) {
// extraction process, but not the side effectful events. Below, we dispatch
// side effectful events.
EventPluginHub.enqueueEvents(extractedEvents);
EventPluginHub.processEventQueue();
EventPluginHub.processEventQueue(true);
// Ensure that every event that declared an `order`, was actually dispatched.
expect(
@@ -15,7 +15,7 @@ var EventPluginHub = require('EventPluginHub');
function runEventQueueInBatch(events) {
EventPluginHub.enqueueEvents(events);
EventPluginHub.processEventQueue();
EventPluginHub.processEventQueue(false);
}
var ReactEventEmitterMixin = {
+26 -18
View File
@@ -14,25 +14,33 @@
var caughtError = null;
var ReactErrorUtils = {
/**
* Call a function while guarding against errors that happens within it.
*
* @param {?String} name of the guard to use for logging or debugging
* @param {Function} func The function to invoke
* @param {*} a First argument
* @param {*} b Second argument
*/
invokeGuardedCallback: function(name, func, a, b) {
try {
return func(a, b);
} catch (x) {
if (caughtError === null) {
caughtError = x;
}
return undefined;
/**
* Call a function while guarding against errors that happens within it.
*
* @param {?String} name of the guard to use for logging or debugging
* @param {Function} func The function to invoke
* @param {*} a First argument
* @param {*} b Second argument
*/
function invokeGuardedCallback(name, func, a, b) {
try {
return func(a, b);
} catch (x) {
if (caughtError === null) {
caughtError = x;
}
},
return undefined;
}
}
var ReactErrorUtils = {
invokeGuardedCallback: invokeGuardedCallback,
/**
* Invoked by ReactTestUtils.Simulate so that any errors thrown by the event
* handler are sure to be rethrown by rethrowCaughtError.
*/
invokeGuardedCallbackWithCatch: invokeGuardedCallback,
/**
* During execution of guarded functions we will capture the first error which
+1 -1
View File
@@ -475,7 +475,7 @@ function makeSimulator(eventType) {
ReactUpdates.batchedUpdates(function() {
EventPluginHub.enqueueEvents(event);
EventPluginHub.processEventQueue();
EventPluginHub.processEventQueue(true);
});
};
}