Skip to content

Commit 7b84041

Browse files
committed
update user event
1 parent 63c5e13 commit 7b84041

File tree

8 files changed

+49
-46
lines changed

8 files changed

+49
-46
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"cSpell.words": [
33
"labelledby",
4+
"Pressability",
45
"Pressable",
56
"redent",
67
"RNTL",

src/user-event/clear.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ export async function clear(this: UserEventInstance, element: ReactTestInstance)
2222
}
2323

2424
// 1. Enter element
25-
dispatchEvent(element, 'focus', EventBuilder.Common.focus());
25+
await dispatchEvent(element, 'focus', EventBuilder.Common.focus());
2626

2727
// 2. Select all
2828
const textToClear = getTextInputValue(element);
2929
const selectionRange = {
3030
start: 0,
3131
end: textToClear.length,
3232
};
33-
dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(selectionRange));
33+
await dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(selectionRange));
3434

3535
// 3. Press backspace with selected text
3636
const emptyText = '';
@@ -42,6 +42,6 @@ export async function clear(this: UserEventInstance, element: ReactTestInstance)
4242

4343
// 4. Exit element
4444
await wait(this.config);
45-
dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(emptyText));
46-
dispatchEvent(element, 'blur', EventBuilder.Common.blur());
45+
await dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(emptyText));
46+
await dispatchEvent(element, 'blur', EventBuilder.Common.blur());
4747
}

src/user-event/paste.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,27 @@ export async function paste(
2626
}
2727

2828
// 1. Enter element
29-
dispatchEvent(element, 'focus', EventBuilder.Common.focus());
29+
await dispatchEvent(element, 'focus', EventBuilder.Common.focus());
3030

3131
// 2. Select all
3232
const textToClear = getTextInputValue(element);
3333
const rangeToClear = { start: 0, end: textToClear.length };
34-
dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(rangeToClear));
34+
await dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(rangeToClear));
3535

3636
// 3. Paste the text
3737
nativeState.valueForElement.set(element, text);
38-
dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));
39-
dispatchEvent(element, 'changeText', text);
38+
await dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));
39+
await dispatchEvent(element, 'changeText', text);
4040

4141
const rangeAfter = { start: text.length, end: text.length };
42-
dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(rangeAfter));
42+
await dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(rangeAfter));
4343

4444
// According to the docs only multiline TextInput emits contentSizeChange event
4545
// @see: https://reactnative.dev/docs/textinput#oncontentsizechange
4646
const isMultiline = element.props.multiline === true;
4747
if (isMultiline) {
4848
const contentSize = getTextContentSize(text);
49-
dispatchEvent(
49+
await dispatchEvent(
5050
element,
5151
'contentSizeChange',
5252
EventBuilder.TextInput.contentSizeChange(contentSize),
@@ -55,6 +55,6 @@ export async function paste(
5555

5656
// 4. Exit element
5757
await wait(this.config);
58-
dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(text));
59-
dispatchEvent(element, 'blur', EventBuilder.Common.blur());
58+
await dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(text));
59+
await dispatchEvent(element, 'blur', EventBuilder.Common.blur());
6060
}

src/user-event/press/press.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,23 +118,23 @@ async function emitDirectPressEvents(
118118
options: BasePressOptions,
119119
) {
120120
await wait(config);
121-
dispatchEvent(element, 'pressIn', EventBuilder.Common.touch());
121+
await dispatchEvent(element, 'pressIn', EventBuilder.Common.touch());
122122

123123
await wait(config, options.duration);
124124

125125
// Long press events are emitted before `pressOut`.
126126
if (options.type === 'longPress') {
127-
dispatchEvent(element, 'longPress', EventBuilder.Common.touch());
127+
await dispatchEvent(element, 'longPress', EventBuilder.Common.touch());
128128
}
129129

130-
dispatchEvent(element, 'pressOut', EventBuilder.Common.touch());
130+
await dispatchEvent(element, 'pressOut', EventBuilder.Common.touch());
131131

132132
// Regular press events are emitted after `pressOut` according to the React Native docs.
133133
// See: https://reactnative.dev/docs/pressable#onpress
134134
// Experimentally for very short presses (< 130ms) `press` events are actually emitted before `onPressOut`, but
135135
// we will ignore that as in reality most pressed would be above the 130ms threshold.
136136
if (options.type === 'press') {
137-
dispatchEvent(element, 'press', EventBuilder.Common.touch());
137+
await dispatchEvent(element, 'press', EventBuilder.Common.touch());
138138
}
139139
}
140140

@@ -145,12 +145,12 @@ async function emitPressabilityPressEvents(
145145
) {
146146
await wait(config);
147147

148-
dispatchEvent(element, 'responderGrant', EventBuilder.Common.responderGrant());
148+
await dispatchEvent(element, 'responderGrant', EventBuilder.Common.responderGrant());
149149

150150
const duration = options.duration ?? DEFAULT_MIN_PRESS_DURATION;
151151
await wait(config, duration);
152152

153-
dispatchEvent(element, 'responderRelease', EventBuilder.Common.responderRelease());
153+
await dispatchEvent(element, 'responderRelease', EventBuilder.Common.responderRelease());
154154

155155
// React Native will wait for minimal delay of DEFAULT_MIN_PRESS_DURATION
156156
// before emitting the `pressOut` event. We need to wait here, so that

src/user-event/scroll/scroll-to.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export async function scrollTo(
5050

5151
ensureScrollViewDirection(element, options);
5252

53-
dispatchEvent(
53+
await dispatchEvent(
5454
element,
5555
'contentSizeChange',
5656
options.contentSize?.width ?? 0,
@@ -88,7 +88,7 @@ async function emitDragScrollEvents(
8888
}
8989

9090
await wait(config);
91-
dispatchEvent(
91+
await dispatchEvent(
9292
element,
9393
'scrollBeginDrag',
9494
EventBuilder.ScrollView.scroll(scrollSteps[0], scrollOptions),
@@ -99,12 +99,12 @@ async function emitDragScrollEvents(
9999
// See: https://github.com/callstack/react-native-testing-library/wiki/ScrollView-Events
100100
for (let i = 1; i < scrollSteps.length - 1; i += 1) {
101101
await wait(config);
102-
dispatchEvent(element, 'scroll', EventBuilder.ScrollView.scroll(scrollSteps[i], scrollOptions));
102+
await dispatchEvent(element, 'scroll', EventBuilder.ScrollView.scroll(scrollSteps[i], scrollOptions));
103103
}
104104

105105
await wait(config);
106106
const lastStep = scrollSteps.at(-1);
107-
dispatchEvent(element, 'scrollEndDrag', EventBuilder.ScrollView.scroll(lastStep, scrollOptions));
107+
await dispatchEvent(element, 'scrollEndDrag', EventBuilder.ScrollView.scroll(lastStep, scrollOptions));
108108
}
109109

110110
async function emitMomentumScrollEvents(
@@ -118,7 +118,7 @@ async function emitMomentumScrollEvents(
118118
}
119119

120120
await wait(config);
121-
dispatchEvent(
121+
await dispatchEvent(
122122
element,
123123
'momentumScrollBegin',
124124
EventBuilder.ScrollView.scroll(scrollSteps[0], scrollOptions),
@@ -129,12 +129,12 @@ async function emitMomentumScrollEvents(
129129
// See: https://github.com/callstack/react-native-testing-library/wiki/ScrollView-Events
130130
for (let i = 1; i < scrollSteps.length; i += 1) {
131131
await wait(config);
132-
dispatchEvent(element, 'scroll', EventBuilder.ScrollView.scroll(scrollSteps[i], scrollOptions));
132+
await dispatchEvent(element, 'scroll', EventBuilder.ScrollView.scroll(scrollSteps[i], scrollOptions));
133133
}
134134

135135
await wait(config);
136136
const lastStep = scrollSteps.at(-1);
137-
dispatchEvent(
137+
await dispatchEvent(
138138
element,
139139
'momentumScrollEnd',
140140
EventBuilder.ScrollView.scroll(lastStep, scrollOptions),

src/user-event/type/type.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ export async function type(
3737
const keys = parseKeys(text);
3838

3939
if (!options?.skipPress) {
40-
dispatchEvent(element, 'pressIn', EventBuilder.Common.touch());
40+
await dispatchEvent(element, 'pressIn', EventBuilder.Common.touch());
4141
}
4242

43-
dispatchEvent(element, 'focus', EventBuilder.Common.focus());
43+
await dispatchEvent(element, 'focus', EventBuilder.Common.focus());
4444

4545
if (!options?.skipPress) {
4646
await wait(this.config);
47-
dispatchEvent(element, 'pressOut', EventBuilder.Common.touch());
47+
await dispatchEvent(element, 'pressOut', EventBuilder.Common.touch());
4848
}
4949

5050
let currentText = getTextInputValue(element);
@@ -66,12 +66,12 @@ export async function type(
6666
await wait(this.config);
6767

6868
if (options?.submitEditing) {
69-
dispatchEvent(element, 'submitEditing', EventBuilder.TextInput.submitEditing(finalText));
69+
await dispatchEvent(element, 'submitEditing', EventBuilder.TextInput.submitEditing(finalText));
7070
}
7171

7272
if (!options?.skipBlur) {
73-
dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(finalText));
74-
dispatchEvent(element, 'blur', EventBuilder.Common.blur());
73+
await dispatchEvent(element, 'endEditing', EventBuilder.TextInput.endEditing(finalText));
74+
await dispatchEvent(element, 'blur', EventBuilder.Common.blur());
7575
}
7676
}
7777

@@ -89,7 +89,7 @@ export async function emitTypingEvents(
8989
const isMultiline = element.props.multiline === true;
9090

9191
await wait(config);
92-
dispatchEvent(element, 'keyPress', EventBuilder.TextInput.keyPress(key));
92+
await dispatchEvent(element, 'keyPress', EventBuilder.TextInput.keyPress(key));
9393

9494
// Platform difference (based on experiments):
9595
// - iOS and RN Web: TextInput emits only `keyPress` event when max length has been reached
@@ -99,20 +99,20 @@ export async function emitTypingEvents(
9999
}
100100

101101
nativeState.valueForElement.set(element, text);
102-
dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));
103-
dispatchEvent(element, 'changeText', text);
102+
await dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));
103+
await dispatchEvent(element, 'changeText', text);
104104

105105
const selectionRange = {
106106
start: text.length,
107107
end: text.length,
108108
};
109-
dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(selectionRange));
109+
await dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(selectionRange));
110110

111111
// According to the docs only multiline TextInput emits contentSizeChange event
112112
// @see: https://reactnative.dev/docs/textinput#oncontentsizechange
113113
if (isMultiline) {
114114
const contentSize = getTextContentSize(text);
115-
dispatchEvent(
115+
await dispatchEvent(
116116
element,
117117
'contentSizeChange',
118118
EventBuilder.TextInput.contentSizeChange(contentSize),

src/user-event/utils/__tests__/dispatch-event.test.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,35 @@ import { dispatchEvent } from '../dispatch-event';
88
const TOUCH_EVENT = EventBuilder.Common.touch();
99

1010
describe('dispatchEvent', () => {
11-
it('does dispatch event', () => {
11+
it('does dispatch event', async () => {
1212
const onPress = jest.fn();
1313
render(<Text testID="text" onPress={onPress} />);
1414

15-
dispatchEvent(screen.getByTestId('text'), 'press', TOUCH_EVENT);
15+
await dispatchEvent(screen.getByTestId('text'), 'press', TOUCH_EVENT);
1616
expect(onPress).toHaveBeenCalledTimes(1);
1717
});
1818

19-
it('does not dispatch event to parent host component', () => {
19+
it('does not dispatch event to parent host component', async () => {
2020
const onPressParent = jest.fn();
2121
render(
2222
<Text onPress={onPressParent}>
2323
<Text testID="text" />
2424
</Text>,
2525
);
2626

27-
dispatchEvent(screen.getByTestId('text'), 'press', TOUCH_EVENT);
27+
await dispatchEvent(screen.getByTestId('text'), 'press', TOUCH_EVENT);
2828
expect(onPressParent).not.toHaveBeenCalled();
2929
});
3030

31-
it('does NOT throw if no handler found', () => {
31+
it('does NOT throw if no handler found', async () => {
3232
render(
3333
<Text>
3434
<Text testID="text" />
3535
</Text>,
3636
);
3737

38-
expect(() => dispatchEvent(screen.getByTestId('text'), 'press', TOUCH_EVENT)).not.toThrow();
38+
await expect(
39+
dispatchEvent(screen.getByTestId('text'), 'press', TOUCH_EVENT),
40+
).resolves.not.toThrow();
3941
});
4042
});

src/user-event/utils/dispatch-event.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { isElementMounted } from '../../helpers/component-tree';
1111
* @param eventName name of the event
1212
* @param event event payload(s)
1313
*/
14-
export function dispatchEvent(element: ReactTestInstance, eventName: string, ...event: unknown[]) {
14+
export async function dispatchEvent(element: ReactTestInstance, eventName: string, ...event: unknown[]) {
1515
if (!isElementMounted(element)) {
1616
return;
1717
}
@@ -21,8 +21,8 @@ export function dispatchEvent(element: ReactTestInstance, eventName: string, ...
2121
return;
2222
}
2323

24-
// This will be called synchronously.
25-
void act(() => {
26-
handler(...event);
24+
// React 19 support: use async act
25+
await act(async () => {
26+
handler(...event)
2727
});
2828
}

0 commit comments

Comments
 (0)