Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feature: fireEvent default event object #1258

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions src/__tests__/fireEvent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
TextInput,
} from 'react-native';
import { render, fireEvent } from '..';
import { defaultPressEvent } from '../helpers/create-event';

type OnPressComponentProps = {
onPress: () => void;
Expand Down Expand Up @@ -104,22 +105,33 @@ describe('fireEvent', () => {
});
});

test('fireEvent.press', () => {
test('fireEvent.press with default event', () => {
const onPressMock = jest.fn();
const text = 'Fireevent press';
const eventData = {
nativeEvent: {
pageX: 20,
pageY: 30,
},
};
const { getByText } = render(
<OnPressComponent onPress={onPressMock} text={text} />
);
const view = render(<Pressable testID="pressable" onPress={onPressMock} />);

fireEvent.press(getByText(text), eventData);
fireEvent.press(view.getByTestId('pressable'));
expect(onPressMock).toHaveBeenCalledWith({ nativeEvent: defaultPressEvent });
});

expect(onPressMock).toHaveBeenCalledWith(eventData);
test('fireEvent.press with default event override', () => {
const onPressMock = jest.fn();
const view = render(<Pressable testID="pressable" onPress={onPressMock} />);

fireEvent.press(view.getByTestId('pressable'), { pageX: 10, pageY: 20 });
expect(onPressMock).toHaveBeenCalledWith({
nativeEvent: { ...defaultPressEvent, pageX: 10, pageY: 20 },
});
});

test('fireEvent.press with explicit event', () => {
const onPressMock = jest.fn();
const view = render(<Pressable testID="pressable" onPress={onPressMock} />);

const event = {
nativeEvent: { pageX: 20, pageY: 30 },
};
fireEvent.press(view.getByTestId('pressable'), event);
expect(onPressMock).toHaveBeenCalledWith(event);
});

test('fireEvent.scroll', () => {
Expand Down
72 changes: 54 additions & 18 deletions src/fireEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TextInput } from 'react-native';
import act from './act';
import { isHostElement } from './helpers/component-tree';
import { filterNodeByType } from './helpers/filterNodeByType';
import { createEvent } from './helpers/create-event';

type EventHandler = (...args: any) => unknown;

Expand Down Expand Up @@ -69,7 +70,6 @@ const isEventEnabled = (
const findEventHandler = (
element: ReactTestInstance,
eventName: string,
callsite?: any,
nearestTouchResponder?: ReactTestInstance
): EventHandler | null => {
const touchResponder = isTouchResponder(element)
Expand All @@ -84,7 +84,7 @@ const findEventHandler = (
return null;
}

return findEventHandler(element.parent, eventName, callsite, touchResponder);
return findEventHandler(element.parent, eventName, touchResponder);
};

const getEventHandler = (
Expand All @@ -106,11 +106,9 @@ const getEventHandler = (
const invokeEvent = (
element: ReactTestInstance,
eventName: string,
callsite?: any,
...data: Array<any>
) => {
const handler = findEventHandler(element, eventName, callsite);

const handler = findEventHandler(element, eventName);
if (!handler) {
return;
}
Expand All @@ -127,23 +125,61 @@ const invokeEvent = (
const toEventHandlerName = (eventName: string) =>
`on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;

const pressHandler = (element: ReactTestInstance, ...data: Array<any>): void =>
invokeEvent(element, 'press', pressHandler, ...data);
const changeTextHandler = (
element: ReactTestInstance,
...data: Array<any>
): void => invokeEvent(element, 'changeText', changeTextHandler, ...data);
const scrollHandler = (element: ReactTestInstance, ...data: Array<any>): void =>
invokeEvent(element, 'scroll', scrollHandler, ...data);
const getCoreEventName = (eventOrHandlerName: string) => {
if (
eventOrHandlerName.startsWith('on') &&
eventOrHandlerName[2] === eventOrHandlerName[2]?.toUpperCase()
) {
const coreName = eventOrHandlerName.slice(2);
return coreName.charAt(0).toLowerCase() + coreName.slice(1);
}

return eventOrHandlerName;
};

const fireEvent = (
element: ReactTestInstance,
eventName: string,
...data: Array<any>
): void => invokeEvent(element, eventName, fireEvent, ...data);
...data: any[]
): void => invokeEvent(element, eventName, ...data);

fireEvent.press = pressHandler;
fireEvent.changeText = changeTextHandler;
fireEvent.scroll = scrollHandler;
function getEventData(eventName: string, ...data: any[]) {
// Legacy mode where user passes 2+ args
if (data.length > 1) {
return data;
}

// Legacy mode where user passes full event object
if (data[0]?.nativeEvent != null) {
return [data[0]];
}

// Mode where user passes optional event init data.
const name = getCoreEventName(eventName);
return [createEvent(name, data[0])];
}

function invokeEventWithDefaultData(
element: ReactTestInstance,
eventName: string,
...data: any[]
) {
const eventData = getEventData(eventName, ...data);
return invokeEvent(element, eventName, ...eventData);
}

// Regular events:
fireEvent.press = (element: ReactTestInstance, ...data: any[]) => {
return invokeEventWithDefaultData(element, 'press', ...data);
};

fireEvent.scroll = (element: ReactTestInstance, ...data: any[]) => {
return invokeEventWithDefaultData(element, 'scroll', ...data);
};

// changeText is not a regular event, as the callback args is just the changed not, and not an Event object
fireEvent.changeText = (element: ReactTestInstance, text: string) => {
return invokeEvent(element, 'changeText', text);
};

export default fireEvent;
35 changes: 35 additions & 0 deletions src/helpers/create-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Based on: https://reactnative.dev/docs/pressevent#example
export const defaultPressEvent = {
changedTouches: [],
identifier: 0,
locationX: 0,
locationY: 0,
pageX: 0,
pageY: 0,
targe: 0,
timestamp: 0,
touches: [],
};

// Based on: https://reactnative.dev/docs/scrollview#onscroll
export const defaultScrollEvent = {
contentInset: { bottom: 0, left: 0, right: 0, top: 0 },
contentOffset: { x: 0, y: 0 },
contentSize: { height: 0, width: 0 },
layoutMeasurement: { height: 0, width: 0 },
zoomScale: 0,
};

const eventMap: Record<string, object> = {
press: { ...defaultPressEvent },
scroll: { ...defaultScrollEvent },
};

export function createEvent(eventName: string, eventInit?: object) {
return {
nativeEvent: {
...eventMap[eventName],
...eventInit,
},
};
}