Repository navigation
Description
Description
This happens only in Jest: it's an interaction between Gesture Handler 3's test stand-in and React Native Testing Library, not a bug in the runtime.
Under Jest, jestSetup.js replaces HostGestureDetector with a plain View (src/mocks/hostDetector.tsx at v3.3.0). For a hook gesture, NativeDetector passes that View its onStartShouldSetResponder (NativeDetector.tsx#L76 at v3.3.0), which declines the responder.
Testing Library 14 treats a declining responder as blocking every event for the ancestors above it, raw touch events included. So fireEvent(child, 'touchStart') under a hook gesture's GestureDetector no longer reaches a parent's onTouchStart. On a device it does. (Once a gesture activates on a device, the ancestors get touchCancel, as expected: that isn't what this is about. In Jest, touchStart and the moves before activation never reach them.) With a builder-API gesture, no such View is rendered and the touch gets through, so tests that passed on 2.x start failing after moving to the hooks.
The root cause appears to be on Testing Library's side: on a device, raw touch events reach every ancestor whatever the responder does, and Testing Library applies the responder rule to them anyway. I've reported it there as callstack/react-native-testing-library#1957. I'm raising it here too because Gesture Handler 3 users will run into it when they move tests from the builder API to the hooks, so you may want to do something about it on your side, or at least be aware of it.
Steps to reproduce
- Clone https://github.com/jrmsayag/rntl-touch-bubbling-repro-demo and run
npm ci. - Run
npx jest. - "with a hook-API gesture (usePanGesture())" fails: the parent's
onTouchStartgets 0 calls. "with a builder-API gesture (Gesture.Pan())" passes.
function Pannable() {
const gesture = usePanGesture({});
return (
<GestureDetector gesture={gesture}>
<View testID="child" />
</GestureDetector>
);
}
it('a raw touchStart under a hook gesture reaches the parent', async () => {
const onTouchStart = jest.fn();
await render(
<GestureHandlerRootView>
<View onTouchStart={onTouchStart}>
<Pannable />
</View>
</GestureHandlerRootView>,
);
await fireEvent(screen.getByTestId('child'), 'touchStart');
expect(onTouchStart).toHaveBeenCalledTimes(1); // fails: 0 calls
});A link to a Gist, an Expo Snack or a link to a repository
https://github.com/jrmsayag/rntl-touch-bubbling-repro-demo
Gesture Handler version
3.3.0
React Native version
0.88.1
Platforms
Android, iOS (it happens in Jest, whatever the platform)
JavaScript runtime
Hermes (not relevant: Jest runs on Node)
Workflow
Using Expo Prebuild or an Expo development build (the repro itself is plain React Native with @react-native/jest-preset)
Build type
Debug mode
Device
Not applicable (Jest); pick any.
Device model
Not applicable (Jest, Node 22). @testing-library/react-native 14.1.0, Jest 30.5.2.
Acknowledgements
Yes
Activity
aliassar commentedon Nov 2, 2026
Can I work on this?