RNTL has two ways to trigger events. Neither goes through React Native's native event system. Both find on* props in the rendered tree and call them inside act().
Both are built on the shared event subsystem in src/events/, which also holds fireEvent itself:
| File | Contents |
|---|---|
fire-event.ts |
Public fireEvent API |
handler.ts |
Finding the on* handler for an event name in props |
propagation.ts |
Bubbling vs direct events, walking up host and composite elements |
is-enabled.ts |
Whether a device would deliver the event: pointerEvents, editable, touch responders |
dispatch.ts |
dispatchEvent(): calls the target's own handler in act(), used by userEvent |
warnings.ts |
eventDiagnostics warnings for fireEvent, and helpers shared with userEvent |
builders/ |
Event payloads, matching what React Native sends on a device |
native-state.ts, update-native-state.ts |
Native state and how fireEvent updates it |
src/user-event/ is a separate module on top of src/events/ and imports it only through src/events/index.ts.
src/events/fire-event.ts is the public API. It calls a single handler for a single event, found with findEventHandler() from src/events/propagation.ts. The work is in finding the right handler:
- It starts at the target and moves up the tree until it finds a handler. It also checks props of composite components, not only host elements.
- Direct events (see Native event propagation) still bubble, with a warning when they reach an ancestor that emits them.
fireEvent.layout()only checks the target. - It mimics cases where a device would not deliver the event, like
pointerEvents, a non-editableTextInput, or a touch responder that declines.
src/user-event/ simulates a whole interaction (press, type, scroll, …) as a realistic sequence of events with delays between them. The sequences are based on how React Native behaves on real devices.
Each step uses dispatchEvent(), which only calls the target's own handler. It doesn't bubble or check whether the element is enabled. Each action does those checks itself, so the rules for an interaction live in one place.
For the eventDiagnostics warning, each action tracks itself with an Interaction from src/user-event/utils/interaction.ts:
- Dispatch events with
interaction.dispatchEvent(), so it records whether any handler ran. Events go tointeraction.target, which is the element the action was called with, unless the action moves it (aspress()does when an ancestor handles the press). If the action has to call a handler itself, record it withinteraction.recordEvent()(aspullToRefresh()does foronRefreshon therefreshControlprop). - Set
hasUpdatedNativeStatewhen the action writes tonativeState. - Add elements that could handle the action but don't accept it to
skippedTargets: disabled, non-editableTextInput, blocked bypointerEvents, or with a responder that declines the touch. The warning first reports the ones blocked bypointerEvents, with the element that blocks them (getPointerEventsBlocker()). Otherwise it reports the disabled ones (computeAriaDisabled(), which includes non-editableTextInput; when all of them are non-editableTextInput, the message calls them non-editable, seeformatDisabledTargets()), and skips the warning if every skipped element has a responder that declines the touch. Text actions (type(),clear(),paste()) add theTextInputwhen it is non-editable or blocked bypointerEvents. - Call
warnAboutUnhandledInteraction()fromsrc/user-event/utils/warnings.tsat the end. It warns only if no handler ran and native state didn't change.
- To change which handler gets a single event, change
fireEvent. To make an interaction more realistic, change theuserEventaction. - Keep
dispatchEvent()simple. - Put event rules that both need, like the
pointerEventsandeditablechecks, insrc/events/. They may build on general helpers fromsrc/helpers/(for exampleisEditableTextInput). Code used only byuserEvent, like delays and scroll steps, stays insrc/user-event/. - Event sequences should match a real device. Check on a device before changing one, and keep the code comments explaining the observed behavior.