Skip to content
Merged
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
1 change: 1 addition & 0 deletions CLAUDE.md
201 changes: 193 additions & 8 deletions src/__tests__/fire-event.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const verticalScrollEvent = { nativeEvent: { contentOffset: { y: 200 } } };
const horizontalScrollEvent = { nativeEvent: { contentOffset: { x: 50 } } };
const pressEventData = { nativeEvent: { pageX: 20, pageY: 30 } };

beforeEach(() => {
jest.spyOn(Date, 'now').mockImplementation(() => 100100100100);
});

test('fireEvent accepts event name with or without "on" prefix', async () => {
const onPress = jest.fn();
await render(<Pressable testID="btn" onPress={onPress} />);
Expand All @@ -36,7 +40,7 @@ test('fireEvent passes event data to handler', async () => {
const onPress = jest.fn();
await render(<Pressable testID="btn" onPress={onPress} />);
await fireEvent.press(screen.getByTestId('btn'), pressEventData);
expect(onPress).toHaveBeenCalledWith(pressEventData);
expect(onPress.mock.calls[0][0]).toMatchObject(pressEventData);
});

test('fireEvent passes multiple parameters to handler', async () => {
Expand All @@ -46,11 +50,11 @@ test('fireEvent passes multiple parameters to handler', async () => {
expect(handlePress).toHaveBeenCalledWith('param1', 'param2', 'param3');
});

test('fireEvent returns handler return value', async () => {
test('fireEvent.press returns undefined when event handler returns a value', async () => {
const handler = jest.fn().mockReturnValue('result');
await render(<Pressable testID="btn" onPress={handler} />);
const result = await fireEvent.press(screen.getByTestId('btn'));
expect(result).toBe('result');
expect(result).toBe(undefined);
});

test('fireEvent bubbles event to parent handler', async () => {
Expand All @@ -65,6 +69,71 @@ test('fireEvent bubbles event to parent handler', async () => {
});

describe('fireEvent.press', () => {
test('passes default press event object to handler', async () => {
const onPress = jest.fn();
await render(<Pressable testID="btn" onPress={onPress} />);
await fireEvent.press(screen.getByTestId('btn'));
expect(onPress.mock.calls[0][0]).toMatchInlineSnapshot(`
{
"currentTarget": {
"measure": [Function],
},
"isDefaultPrevented": [Function],
"isPersistent": [Function],
"isPropagationStopped": [Function],
"nativeEvent": {
"changedTouches": [],
"identifier": 0,
"locationX": 0,
"locationY": 0,
"pageX": 0,
"pageY": 0,
"target": 0,
"timestamp": 100100100100,
"touches": [],
},
"persist": [Function],
"preventDefault": [Function],
"stopPropagation": [Function],
"target": {},
"timeStamp": 0,
}
`);
});

test('overrides default event properties with passed event props', async () => {
const onPress = jest.fn();
await render(<Pressable testID="btn" onPress={onPress} />);
const customEventData = { nativeEvent: { pageX: 20, pageY: 30 } };
await fireEvent.press(screen.getByTestId('btn'), customEventData);
expect(onPress.mock.calls[0][0]).toMatchInlineSnapshot(`
{
"currentTarget": {
"measure": [Function],
},
"isDefaultPrevented": [Function],
"isPersistent": [Function],
"isPropagationStopped": [Function],
"nativeEvent": {
"changedTouches": [],
"identifier": 0,
"locationX": 0,
"locationY": 0,
"pageX": 20,
"pageY": 30,
"target": 0,
"timestamp": 100100100100,
"touches": [],
},
"persist": [Function],
"preventDefault": [Function],
"stopPropagation": [Function],
"target": {},
"timeStamp": 0,
}
`);
});

test.each([
['Pressable', Pressable],
['TouchableOpacity', TouchableOpacity],
Expand Down Expand Up @@ -106,6 +175,107 @@ describe('fireEvent.changeText', () => {
});

describe('fireEvent.scroll', () => {
test('passes default scroll event object to handler', async () => {
const onScroll = jest.fn();
await render(
<ScrollView testID="scroll" onScroll={onScroll}>
<Text>Content</Text>
</ScrollView>,
);
const scrollView = screen.getByTestId('scroll');
await fireEvent.scroll(scrollView);
expect(onScroll.mock.calls[0][0]).toMatchInlineSnapshot(`
{
"currentTarget": {},
"isDefaultPrevented": [Function],
"isPersistent": [Function],
"isPropagationStopped": [Function],
"nativeEvent": {
"contentInset": {
"bottom": 0,
"left": 0,
"right": 0,
"top": 0,
},
"contentOffset": {
"x": 0,
"y": 0,
},
"contentSize": {
"height": 0,
"width": 0,
},
"layoutMeasurement": {
"height": 0,
"width": 0,
},
"responderIgnoreScroll": true,
"target": 0,
"velocity": {
"x": 0,
"y": 0,
},
},
"persist": [Function],
"preventDefault": [Function],
"stopPropagation": [Function],
"target": {},
"timeStamp": 0,
}
`);
});

test('overrides default event properties with passed event props', async () => {
const onScroll = jest.fn();
await render(
<ScrollView testID="scroll" onScroll={onScroll}>
<Text>Content</Text>
</ScrollView>,
);
const scrollView = screen.getByTestId('scroll');
const customEventData = { nativeEvent: { contentOffset: { x: 50, y: 200 } } };
await fireEvent.scroll(scrollView, customEventData);
expect(onScroll.mock.calls[0][0]).toMatchInlineSnapshot(`
{
"currentTarget": {},
"isDefaultPrevented": [Function],
"isPersistent": [Function],
"isPropagationStopped": [Function],
"nativeEvent": {
"contentInset": {
"bottom": 0,
"left": 0,
"right": 0,
"top": 0,
},
"contentOffset": {
"x": 50,
"y": 200,
},
"contentSize": {
"height": 0,
"width": 0,
},
"layoutMeasurement": {
"height": 0,
"width": 0,
},
"responderIgnoreScroll": true,
"target": 0,
"velocity": {
"x": 0,
"y": 0,
},
},
"persist": [Function],
"preventDefault": [Function],
"stopPropagation": [Function],
"target": {},
"timeStamp": 0,
}
`);
});

test('works on ScrollView', async () => {
const onScroll = jest.fn();
await render(
Expand All @@ -115,7 +285,7 @@ describe('fireEvent.scroll', () => {
);
const scrollView = screen.getByTestId('scroll');
await fireEvent.scroll(scrollView, verticalScrollEvent);
expect(onScroll).toHaveBeenCalledWith(verticalScrollEvent);
expect(onScroll.mock.calls[0][0]).toMatchObject(verticalScrollEvent);
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 200 });
});

Expand All @@ -134,7 +304,7 @@ describe('fireEvent.scroll', () => {
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 200 });
});

test('without contentOffset does not update native state', async () => {
test('without contentOffset scrolls to (0, 0)', async () => {
const onScroll = jest.fn();
await render(
<ScrollView testID="scroll" onScroll={onScroll}>
Expand All @@ -143,8 +313,10 @@ describe('fireEvent.scroll', () => {
);
const scrollView = screen.getByTestId('scroll');
await fireEvent.scroll(scrollView, {});
expect(onScroll).toHaveBeenCalled();
expect(nativeState.contentOffsetForElement.get(scrollView)).toBeUndefined();
expect(onScroll.mock.calls[0][0]).toMatchObject({
nativeEvent: { contentOffset: { x: 0, y: 0 } },
});
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 0 });
});

test('with non-finite contentOffset values uses 0', async () => {
Expand All @@ -171,10 +343,23 @@ describe('fireEvent.scroll', () => {
);
const scrollView = screen.getByTestId('scroll');
await fireEvent.scroll(scrollView, horizontalScrollEvent);
expect(onScroll).toHaveBeenCalledWith(horizontalScrollEvent);
expect(onScroll.mock.calls[0][0]).toMatchObject(horizontalScrollEvent);
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 50, y: 0 });
});

test('without contentOffset via fireEvent() does not update native state', async () => {
const onScroll = jest.fn();
await render(
<ScrollView testID="scroll" onScroll={onScroll}>
<Text>Content</Text>
</ScrollView>,
);
const scrollView = screen.getByTestId('scroll');
await fireEvent(scrollView, 'scroll', { nativeEvent: {} });
expect(onScroll).toHaveBeenCalled();
expect(nativeState.contentOffsetForElement.get(scrollView)).toBeUndefined();
});

test('with non-finite x contentOffset value uses 0', async () => {
const onScroll = jest.fn();
await render(
Expand Down
57 changes: 57 additions & 0 deletions src/event-builder/__tests__/common.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
buildBlurEvent,
buildFocusEvent,
buildResponderGrantEvent,
buildResponderReleaseEvent,
buildTouchEvent,
} from '../common';

test('buildTouchEvent returns event with touch nativeEvent', () => {
const event = buildTouchEvent();

expect(event.nativeEvent).toEqual({
changedTouches: [],
identifier: 0,
locationX: 0,
locationY: 0,
pageX: 0,
pageY: 0,
target: 0,
timestamp: expect.any(Number),
touches: [],
});
expect(event.currentTarget).toHaveProperty('measure');
expect(event).toHaveProperty('preventDefault');
});

test('buildResponderGrantEvent returns touch event with dispatchConfig', () => {
const event = buildResponderGrantEvent();

expect(event.dispatchConfig).toEqual({
registrationName: 'onResponderGrant',
});
expect(event.nativeEvent).toHaveProperty('touches');
});

test('buildResponderReleaseEvent returns touch event with dispatchConfig', () => {
const event = buildResponderReleaseEvent();

expect(event.dispatchConfig).toEqual({
registrationName: 'onResponderRelease',
});
expect(event.nativeEvent).toHaveProperty('touches');
});

test('buildFocusEvent returns event with target', () => {
const event = buildFocusEvent();

expect(event.nativeEvent).toEqual({ target: 0 });
expect(event).toHaveProperty('preventDefault');
});

test('buildBlurEvent returns event with target', () => {
const event = buildBlurEvent();

expect(event.nativeEvent).toEqual({ target: 0 });
expect(event).toHaveProperty('preventDefault');
});
16 changes: 16 additions & 0 deletions src/event-builder/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as eventBuilder from '..';

test('re-exports all event builders', () => {
expect(eventBuilder.buildTouchEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildResponderGrantEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildResponderReleaseEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildFocusEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildBlurEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildScrollEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildTextChangeEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildKeyPressEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildSubmitEditingEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildEndEditingEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildTextSelectionChangeEvent).toBeInstanceOf(Function);
expect(eventBuilder.buildContentSizeChangeEvent).toBeInstanceOf(Function);
});
34 changes: 34 additions & 0 deletions src/event-builder/__tests__/scroll.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { buildScrollEvent } from '../scroll';

test('buildScrollEvent returns default scroll event', () => {
const event = buildScrollEvent();

expect(event.nativeEvent).toEqual({
contentInset: { bottom: 0, left: 0, right: 0, top: 0 },
contentOffset: { y: 0, x: 0 },
contentSize: { height: 0, width: 0 },
layoutMeasurement: { height: 0, width: 0 },
responderIgnoreScroll: true,
target: 0,
velocity: { y: 0, x: 0 },
});
});

test('buildScrollEvent uses provided offset', () => {
const event = buildScrollEvent({ y: 100, x: 50 });

expect(event.nativeEvent.contentOffset).toEqual({ y: 100, x: 50 });
});

test('buildScrollEvent uses provided options', () => {
const event = buildScrollEvent(
{ y: 0, x: 0 },
{
contentSize: { height: 1000, width: 400 },
layoutMeasurement: { height: 800, width: 400 },
},
);

expect(event.nativeEvent.contentSize).toEqual({ height: 1000, width: 400 });
expect(event.nativeEvent.layoutMeasurement).toEqual({ height: 800, width: 400 });
});
Loading