diff --git a/packages/react-native-gesture-handler/src/__tests__/mocks.test.tsx b/packages/react-native-gesture-handler/src/__tests__/mocks.test.tsx
index b226005b10..2cc05f6073 100644
--- a/packages/react-native-gesture-handler/src/__tests__/mocks.test.tsx
+++ b/packages/react-native-gesture-handler/src/__tests__/mocks.test.tsx
@@ -24,7 +24,7 @@ import {
TouchableOpacity,
TouchableWithoutFeedback,
} from '../mocks/Touchables';
-import { Touchable } from '../v3/components';
+import { Pressable, Touchable } from '../v3/components';
describe('Jest mocks – legacy components render without crashing', () => {
test('LegacyRawButton', () => {
@@ -142,3 +142,34 @@ test('Trigger Touchable press', () => {
expect(onPress).toHaveBeenCalled();
});
+
+test('Pressable exposes its press handlers to testing-library', () => {
+ // `Pressable` renders `PressableWithTouchable` unless a relation prop is
+ // passed. Both engines have to forward the handlers as `testOnly_*` props on
+ // the button, since that is what `fireEvent(element, 'press')` picks up in a
+ // test environment — the press state machine itself runs natively.
+ const onPress = jest.fn();
+ const onPressIn = jest.fn();
+ const onPressOut = jest.fn();
+ const onLongPress = jest.fn();
+
+ render(
+
+
+ Press Me
+
+
+ );
+
+ const button = screen.getByTestId('pressable');
+
+ expect(button.props.testOnly_onPress).toBe(onPress);
+ expect(button.props.testOnly_onPressIn).toBe(onPressIn);
+ expect(button.props.testOnly_onPressOut).toBe(onPressOut);
+ expect(button.props.testOnly_onLongPress).toBe(onLongPress);
+});
diff --git a/packages/react-native-gesture-handler/src/v3/components/PressableWithTouchable.tsx b/packages/react-native-gesture-handler/src/v3/components/PressableWithTouchable.tsx
index 923cc61ab6..03ac35b79f 100644
--- a/packages/react-native-gesture-handler/src/v3/components/PressableWithTouchable.tsx
+++ b/packages/react-native-gesture-handler/src/v3/components/PressableWithTouchable.tsx
@@ -13,9 +13,12 @@ import {
numberAsInset,
} from '../../components/Pressable/utils';
import { PressabilityDebugView } from '../../handlers/PressabilityDebugView';
+import { isTestEnv } from '../../utils';
import { pointerStyle } from './pointerStyle';
import { Touchable } from './Touchable/Touchable';
+const IS_TEST_ENV = isTestEnv();
+
// RN's Pressable default. Touchable's own default is 600ms
const DEFAULT_LONG_PRESS_DURATION = 500;
@@ -314,7 +317,11 @@ const PressableWithTouchable = (props: PressableProps) => {
onPress={handlePress}
onLongPress={handleLongPress}
onHoverIn={handleHoverIn}
- onHoverOut={handleHoverOut}>
+ onHoverOut={handleHoverOut}
+ testOnly_onPress={IS_TEST_ENV ? onPress : undefined}
+ testOnly_onPressIn={IS_TEST_ENV ? onPressIn : undefined}
+ testOnly_onPressOut={IS_TEST_ENV ? onPressOut : undefined}
+ testOnly_onLongPress={IS_TEST_ENV ? onLongPress : undefined}>
{resolvedChildren}
{__DEV__ ? (