Skip to content

Commit 674b2f9

Browse files
refactor: fireEvent cleanup (#1401)
1 parent 72e3133 commit 674b2f9

File tree

2 files changed

+67
-68
lines changed

2 files changed

+67
-68
lines changed

src/__tests__/fireEvent.test.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ test('should not fire inside View with pointerEvents="none"', () => {
265265
);
266266

267267
fireEvent.press(screen.getByText('Trigger'));
268+
fireEvent(screen.getByText('Trigger'), 'onPress');
268269
expect(onPress).not.toHaveBeenCalled();
269270
});
270271

@@ -279,6 +280,7 @@ test('should not fire inside View with pointerEvents="box-only"', () => {
279280
);
280281

281282
fireEvent.press(screen.getByText('Trigger'));
283+
fireEvent(screen.getByText('Trigger'), 'onPress');
282284
expect(onPress).not.toHaveBeenCalled();
283285
});
284286

@@ -293,7 +295,8 @@ test('should fire inside View with pointerEvents="box-none"', () => {
293295
);
294296

295297
fireEvent.press(screen.getByText('Trigger'));
296-
expect(onPress).toHaveBeenCalled();
298+
fireEvent(screen.getByText('Trigger'), 'onPress');
299+
expect(onPress).toHaveBeenCalledTimes(2);
297300
});
298301

299302
test('should fire inside View with pointerEvents="auto"', () => {
@@ -307,7 +310,8 @@ test('should fire inside View with pointerEvents="auto"', () => {
307310
);
308311

309312
fireEvent.press(screen.getByText('Trigger'));
310-
expect(onPress).toHaveBeenCalled();
313+
fireEvent(screen.getByText('Trigger'), 'onPress');
314+
expect(onPress).toHaveBeenCalledTimes(2);
311315
});
312316

313317
test('should not fire deeply inside View with pointerEvents="box-only"', () => {
@@ -323,6 +327,7 @@ test('should not fire deeply inside View with pointerEvents="box-only"', () => {
323327
);
324328

325329
fireEvent.press(screen.getByText('Trigger'));
330+
fireEvent(screen.getByText('Trigger'), 'onPress');
326331
expect(onPress).not.toHaveBeenCalled();
327332
});
328333

src/fireEvent.ts

Lines changed: 60 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,9 @@ import { getHostParent, isHostElement } from './helpers/component-tree';
55
import { filterNodeByType } from './helpers/filterNodeByType';
66
import { getHostComponentNames } from './helpers/host-component-names';
77

8-
type EventHandler = (...args: any) => unknown;
9-
10-
const isTextInput = (element?: ReactTestInstance) => {
11-
if (!element) {
12-
return false;
13-
}
8+
type EventHandler = (...args: unknown[]) => unknown;
149

10+
function isTextInput(element: ReactTestInstance) {
1511
// We have to test if the element type is either the `TextInput` component
1612
// (for composite component) or the string "TextInput" (for host component)
1713
// All queries return host components but since fireEvent bubbles up
@@ -20,18 +16,22 @@ const isTextInput = (element?: ReactTestInstance) => {
2016
filterNodeByType(element, TextInput) ||
2117
filterNodeByType(element, getHostComponentNames().textInput)
2218
);
23-
};
19+
}
2420

25-
const isTouchResponder = (element?: ReactTestInstance) => {
26-
if (!isHostElement(element)) return false;
21+
function isTouchResponder(element: ReactTestInstance) {
22+
if (!isHostElement(element)) {
23+
return false;
24+
}
2725

28-
return !!element?.props.onStartShouldSetResponder || isTextInput(element);
29-
};
26+
return (
27+
Boolean(element.props.onStartShouldSetResponder) || isTextInput(element)
28+
);
29+
}
3030

31-
const isPointerEventEnabled = (
31+
function isPointerEventEnabled(
3232
element: ReactTestInstance,
3333
isParent?: boolean
34-
): boolean => {
34+
): boolean {
3535
const pointerEvents = element.props.pointerEvents;
3636
if (pointerEvents === 'none') {
3737
return false;
@@ -47,54 +47,60 @@ const isPointerEventEnabled = (
4747
}
4848

4949
return isPointerEventEnabled(parent, true);
50-
};
50+
}
51+
52+
// Due to accepting both `press` and `onPress` for event names, we need to
53+
// cover both forms.
54+
const touchEventNames = ['press', 'onPress'];
5155

52-
const isTouchEvent = (eventName?: string) => {
53-
return eventName === 'press';
54-
};
56+
function isTouchEvent(eventName: string) {
57+
return touchEventNames.includes(eventName);
58+
}
5559

56-
const isEventEnabled = (
60+
function isEventEnabled(
5761
element: ReactTestInstance,
58-
touchResponder?: ReactTestInstance,
59-
eventName?: string
60-
) => {
61-
if (isTextInput(element)) return element?.props.editable !== false;
62-
if (!isPointerEventEnabled(element) && isTouchEvent(eventName)) return false;
62+
eventName: string,
63+
nearestTouchResponder?: ReactTestInstance
64+
) {
65+
if (isTextInput(element)) {
66+
return element.props.editable !== false;
67+
}
6368

64-
const touchStart = touchResponder?.props.onStartShouldSetResponder?.();
65-
const touchMove = touchResponder?.props.onMoveShouldSetResponder?.();
69+
if (isTouchEvent(eventName) && !isPointerEventEnabled(element)) {
70+
return false;
71+
}
6672

67-
if (touchStart || touchMove) return true;
73+
const touchStart = nearestTouchResponder?.props.onStartShouldSetResponder?.();
74+
const touchMove = nearestTouchResponder?.props.onMoveShouldSetResponder?.();
75+
if (touchStart || touchMove) {
76+
return true;
77+
}
6878

6979
return touchStart === undefined && touchMove === undefined;
70-
};
80+
}
7181

72-
const findEventHandler = (
82+
function findEventHandler(
7383
element: ReactTestInstance,
7484
eventName: string,
75-
callsite?: any,
7685
nearestTouchResponder?: ReactTestInstance
77-
): EventHandler | null => {
86+
): EventHandler | null {
7887
const touchResponder = isTouchResponder(element)
7988
? element
8089
: nearestTouchResponder;
8190

8291
const handler = getEventHandler(element, eventName);
83-
if (handler && isEventEnabled(element, touchResponder, eventName))
92+
if (handler && isEventEnabled(element, eventName, touchResponder))
8493
return handler;
8594

8695
if (element.parent === null || element.parent.parent === null) {
8796
return null;
8897
}
8998

90-
return findEventHandler(element.parent, eventName, callsite, touchResponder);
91-
};
99+
return findEventHandler(element.parent, eventName, touchResponder);
100+
}
92101

93-
const getEventHandler = (
94-
element: ReactTestInstance,
95-
eventName: string
96-
): EventHandler | undefined => {
97-
const eventHandlerName = toEventHandlerName(eventName);
102+
function getEventHandler(element: ReactTestInstance, eventName: string) {
103+
const eventHandlerName = getEventHandlerName(eventName);
98104
if (typeof element.props[eventHandlerName] === 'function') {
99105
return element.props[eventHandlerName];
100106
}
@@ -104,49 +110,37 @@ const getEventHandler = (
104110
}
105111

106112
return undefined;
107-
};
113+
}
114+
115+
function getEventHandlerName(eventName: string) {
116+
return `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;
117+
}
108118

109-
const invokeEvent = (
119+
function fireEvent(
110120
element: ReactTestInstance,
111121
eventName: string,
112-
callsite?: any,
113-
...data: Array<any>
114-
) => {
115-
const handler = findEventHandler(element, eventName, callsite);
116-
122+
...data: unknown[]
123+
) {
124+
const handler = findEventHandler(element, eventName);
117125
if (!handler) {
118126
return;
119127
}
120128

121129
let returnValue;
122-
123130
act(() => {
124131
returnValue = handler(...data);
125132
});
126133

127134
return returnValue;
128-
};
129-
130-
const toEventHandlerName = (eventName: string) =>
131-
`on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;
135+
}
132136

133-
const pressHandler = (element: ReactTestInstance, ...data: Array<any>): void =>
134-
invokeEvent(element, 'press', pressHandler, ...data);
135-
const changeTextHandler = (
136-
element: ReactTestInstance,
137-
...data: Array<any>
138-
): void => invokeEvent(element, 'changeText', changeTextHandler, ...data);
139-
const scrollHandler = (element: ReactTestInstance, ...data: Array<any>): void =>
140-
invokeEvent(element, 'scroll', scrollHandler, ...data);
137+
fireEvent.press = (element: ReactTestInstance, ...data: unknown[]) =>
138+
fireEvent(element, 'press', ...data);
141139

142-
const fireEvent = (
143-
element: ReactTestInstance,
144-
eventName: string,
145-
...data: Array<any>
146-
): void => invokeEvent(element, eventName, fireEvent, ...data);
140+
fireEvent.changeText = (element: ReactTestInstance, ...data: unknown[]) =>
141+
fireEvent(element, 'changeText', ...data);
147142

148-
fireEvent.press = pressHandler;
149-
fireEvent.changeText = changeTextHandler;
150-
fireEvent.scroll = scrollHandler;
143+
fireEvent.scroll = (element: ReactTestInstance, ...data: unknown[]) =>
144+
fireEvent(element, 'scroll', ...data);
151145

152146
export default fireEvent;

0 commit comments

Comments
 (0)