diff --git a/src/__tests__/fireEvent.test.js b/src/__tests__/fireEvent.test.js
index 74d97aa9e..5fef1c806 100644
--- a/src/__tests__/fireEvent.test.js
+++ b/src/__tests__/fireEvent.test.js
@@ -282,3 +282,26 @@ test('is not fooled by non-native disabled prop', () => {
fireEvent.press(screen.getByText('Trigger Test'));
expect(handlePress).toHaveBeenCalledTimes(1);
});
+
+function TestChildTouchableComponent({ onPress, someProp }) {
+ return (
+
+
+ Trigger
+
+
+ );
+}
+
+test('is not fooled by non-responder wrapping host elements', () => {
+ const handlePress = jest.fn();
+
+ const screen = render(
+
+
+
+ );
+
+ fireEvent.press(screen.getByText('Trigger'));
+ expect(handlePress).not.toHaveBeenCalled();
+});
diff --git a/src/fireEvent.js b/src/fireEvent.js
index 08b9a65ec..c9156dd13 100644
--- a/src/fireEvent.js
+++ b/src/fireEvent.js
@@ -2,32 +2,47 @@
import act from './act';
import { ErrorWithStack } from './helpers/errors';
-const isTextInputComponent = (element: ReactTestInstance) => {
+const isHostElement = (element?: ReactTestInstance) => {
+ return typeof element?.type === 'string';
+};
+
+const isTextInput = (element?: ReactTestInstance) => {
// eslint-disable-next-line import/no-extraneous-dependencies
const { TextInput } = require('react-native');
- return element.type === TextInput;
+ return element?.type === TextInput;
+};
+
+const isTouchResponder = (element?: ReactTestInstance) => {
+ if (!isHostElement(element)) return false;
+
+ return !!element?.props.onStartShouldSetResponder || isTextInput(element);
+};
+
+const isEventEnabled = (
+ element?: ReactTestInstance,
+ touchResponder?: ReactTestInstance
+) => {
+ if (isTextInput(element)) return element?.props.editable !== false;
+
+ return touchResponder?.props.onStartShouldSetResponder?.() !== false;
};
const findEventHandler = (
element: ReactTestInstance,
eventName: string,
callsite?: any,
- nearestHostDescendent?: ReactTestInstance,
+ nearestTouchResponder?: ReactTestInstance,
hasDescendandHandler?: boolean
) => {
- const handler = getEventHandler(element, eventName);
- const hasHandler = handler != null || hasDescendandHandler;
-
- const isHostComponent = typeof element.type === 'string';
- const hostElement = isHostComponent ? element : nearestHostDescendent;
+ const touchResponder = isTouchResponder(element)
+ ? element
+ : nearestTouchResponder;
- const isEventEnabled = isTextInputComponent(element)
- ? element.props.editable !== false
- : hostElement?.props.onStartShouldSetResponder?.() !== false;
-
- if (handler && isEventEnabled) return handler;
+ const handler = getEventHandler(element, eventName);
+ if (handler && isEventEnabled(element, touchResponder)) return handler;
// Do not bubble event to the root element
+ const hasHandler = handler != null || hasDescendandHandler;
if (element.parent === null || element.parent.parent === null) {
if (hasHandler) {
return null;
@@ -43,7 +58,7 @@ const findEventHandler = (
element.parent,
eventName,
callsite,
- hostElement,
+ touchResponder,
hasHandler
);
};