Simple and complete React Native testing utilities that encourage good testing practices
—
Realistic user interaction simulation supporting press events, text input, scrolling, and other React Native-specific gestures. Provides both synchronous fireEvent API and more realistic asynchronous userEvent API.
Synchronous event firing utility for immediate event simulation - faster but less realistic than userEvent.
/**
* Synchronous event firing utility
* Fires events immediately without realistic timing or validation
*/
declare const fireEvent: {
/** Fire press event on pressable elements */
press(element: ReactTestInstance): void;
/** Fire long press event */
longPress(element: ReactTestInstance): void;
/** Fire text change event on TextInput elements */
changeText(element: ReactTestInstance, text: string): void;
/** Fire scroll event on scrollable elements */
scroll(element: ReactTestInstance, eventData?: ScrollEventData): void;
/** Fire focus event on focusable elements */
focus(element: ReactTestInstance): void;
/** Fire blur event on focusable elements */
blur(element: ReactTestInstance): void;
/** Fire layout event */
layout(element: ReactTestInstance, layoutData: LayoutEventData): void;
/** Fire content size change event */
contentSizeChange(element: ReactTestInstance, contentSize: ContentSizeData): void;
/** Fire end editing event on TextInput */
endEditing(element: ReactTestInstance, text?: string): void;
/** Fire selection change event on TextInput */
selectionChange(element: ReactTestInstance, selection: SelectionData): void;
/** Fire refresh event on RefreshControl */
refresh(element: ReactTestInstance): void;
/** Fire momentum scroll begin event */
momentumScrollBegin(element: ReactTestInstance): void;
/** Fire momentum scroll end event */
momentumScrollEnd(element: ReactTestInstance): void;
/** Fire scroll begin drag event */
scrollBeginDrag(element: ReactTestInstance): void;
/** Fire scroll end drag event */
scrollEndDrag(element: ReactTestInstance): void;
};
/**
* Async version of fireEvent for more realistic timing
*/
declare const fireEventAsync: typeof fireEvent;
interface ScrollEventData {
nativeEvent: {
contentOffset: { x: number; y: number };
contentSize: { width: number; height: number };
layoutMeasurement: { width: number; height: number };
};
}
interface LayoutEventData {
nativeEvent: {
layout: { x: number; y: number; width: number; height: number };
};
}
interface ContentSizeData {
nativeEvent: {
contentSize: { width: number; height: number };
};
}
interface SelectionData {
nativeEvent: {
selection: { start: number; end: number };
};
}Usage Examples:
import { render, screen, fireEvent } from "@testing-library/react-native";
test("fireEvent interactions", () => {
const handlePress = jest.fn();
const handleTextChange = jest.fn();
const handleScroll = jest.fn();
render(
<View>
<Pressable onPress={handlePress} testID="button">
<Text>Press Me</Text>
</Pressable>
<TextInput
onChangeText={handleTextChange}
testID="input"
placeholder="Type here"
/>
<ScrollView onScroll={handleScroll} testID="scroll">
<Text>Scrollable content</Text>
</ScrollView>
</View>
);
// Press button
const button = screen.getByTestId("button");
fireEvent.press(button);
expect(handlePress).toHaveBeenCalledTimes(1);
// Change text
const input = screen.getByTestId("input");
fireEvent.changeText(input, "Hello World");
expect(handleTextChange).toHaveBeenCalledWith("Hello World");
// Scroll
const scrollView = screen.getByTestId("scroll");
fireEvent.scroll(scrollView, {
nativeEvent: {
contentOffset: { x: 0, y: 100 },
contentSize: { width: 300, height: 1000 },
layoutMeasurement: { width: 300, height: 400 }
}
});
expect(handleScroll).toHaveBeenCalled();
// Long press
fireEvent.longPress(button);
// Focus and blur
fireEvent.focus(input);
fireEvent.blur(input);
// End editing
fireEvent.endEditing(input, "Final text");
});More realistic user interaction simulation with proper timing, validation, and event sequences.
/**
* Realistic user event simulation
* Provides more authentic user interactions with proper timing and validation
*/
declare const userEvent: {
/** Create configured user event instance */
setup(config?: UserEventConfig): UserEventInstance;
// Direct access methods for v13 compatibility
/** Press an element */
press(element: ReactTestInstance): Promise<void>;
/** Long press an element with optional configuration */
longPress(element: ReactTestInstance, options?: PressOptions): Promise<void>;
/** Type text into an input element */
type(element: ReactTestInstance, text: string, options?: TypeOptions): Promise<void>;
/** Clear text from an input element */
clear(element: ReactTestInstance): Promise<void>;
/** Paste text into an input element */
paste(element: ReactTestInstance, text: string): Promise<void>;
/** Scroll an element to specific position */
scrollTo(element: ReactTestInstance, options: ScrollToOptions): Promise<void>;
};
interface UserEventConfig {
/** Delay between keystrokes when typing (ms) */
delay?: number;
/** Whether to skip validation of element state */
skipValidation?: boolean;
/** Custom press duration for long press (ms) */
longPressDelay?: number;
}
interface UserEventInstance {
/** Press an element */
press(element: ReactTestInstance): Promise<void>;
/** Long press with options */
longPress(element: ReactTestInstance, options?: PressOptions): Promise<void>;
/** Type text with realistic timing */
type(element: ReactTestInstance, text: string, options?: TypeOptions): Promise<void>;
/** Clear text input */
clear(element: ReactTestInstance): Promise<void>;
/** Paste text */
paste(element: ReactTestInstance, text: string): Promise<void>;
/** Scroll to position */
scrollTo(element: ReactTestInstance, options: ScrollToOptions): Promise<void>;
}Usage Examples:
import { render, screen, userEvent } from "@testing-library/react-native";
test("userEvent interactions", async () => {
const user = userEvent.setup({ delay: 10 });
const handlePress = jest.fn();
const handleTextChange = jest.fn();
render(
<View>
<Pressable onPress={handlePress} testID="button">
<Text>Press Me</Text>
</Pressable>
<TextInput
onChangeText={handleTextChange}
testID="input"
placeholder="Type here"
/>
</View>
);
// Realistic button press
const button = screen.getByTestId("button");
await user.press(button);
expect(handlePress).toHaveBeenCalledTimes(1);
// Realistic text typing with delays
const input = screen.getByTestId("input");
await user.type(input, "Hello World");
expect(input.props.value).toBe("Hello World");
// Clear input
await user.clear(input);
expect(input.props.value).toBe("");
// Paste text
await user.paste(input, "Pasted content");
expect(input.props.value).toBe("Pasted content");
// Long press with custom duration
await user.longPress(button, { duration: 1000 });
});
test("direct userEvent methods", async () => {
render(<Pressable testID="button"><Text>Press</Text></Pressable>);
// Direct method usage (v13 compatibility)
const button = screen.getByTestId("button");
await userEvent.press(button);
await userEvent.longPress(button);
});Detailed press interaction options and configurations.
/**
* Press an element with realistic touch behavior
* @param element - Element to press
* @returns Promise that resolves when press is complete
*/
function press(element: ReactTestInstance): Promise<void>;
/**
* Long press an element with configurable duration
* @param element - Element to long press
* @param options - Long press configuration options
* @returns Promise that resolves when long press is complete
*/
function longPress(element: ReactTestInstance, options?: PressOptions): Promise<void>;
interface PressOptions {
/** Duration of long press in milliseconds (default: 500) */
duration?: number;
}Usage Examples:
test("press interactions", async () => {
const user = userEvent.setup();
const handlePress = jest.fn();
const handleLongPress = jest.fn();
render(
<Pressable
onPress={handlePress}
onLongPress={handleLongPress}
testID="interactive-button"
>
<Text>Interactive Button</Text>
</Pressable>
);
const button = screen.getByTestId("interactive-button");
// Standard press
await user.press(button);
expect(handlePress).toHaveBeenCalledTimes(1);
// Long press with default duration (500ms)
await user.longPress(button);
expect(handleLongPress).toHaveBeenCalledTimes(1);
// Long press with custom duration
await user.longPress(button, { duration: 1000 });
expect(handleLongPress).toHaveBeenCalledTimes(2);
});Comprehensive text input simulation with realistic typing, clearing, and pasting.
/**
* Type text into an input element with realistic timing
* @param element - TextInput element to type into
* @param text - Text to type
* @param options - Typing configuration options
* @returns Promise that resolves when typing is complete
*/
function type(element: ReactTestInstance, text: string, options?: TypeOptions): Promise<void>;
/**
* Clear all text from an input element
* @param element - TextInput element to clear
* @returns Promise that resolves when clearing is complete
*/
function clear(element: ReactTestInstance): Promise<void>;
/**
* Paste text into an input element
* @param element - TextInput element to paste into
* @param text - Text to paste
* @returns Promise that resolves when pasting is complete
*/
function paste(element: ReactTestInstance, text: string): Promise<void>;
interface TypeOptions {
/** Delay between keystrokes in milliseconds */
delay?: number;
/** Whether to skip element validation */
skipValidation?: boolean;
/** Whether to clear existing text before typing */
clearFirst?: boolean;
}Usage Examples:
test("text input interactions", async () => {
const user = userEvent.setup();
const handleTextChange = jest.fn();
const handleSelectionChange = jest.fn();
render(
<TextInput
testID="text-input"
placeholder="Enter text"
onChangeText={handleTextChange}
onSelectionChange={handleSelectionChange}
multiline
/>
);
const input = screen.getByTestId("text-input");
// Type with default timing
await user.type(input, "Hello");
expect(handleTextChange).toHaveBeenLastCalledWith("Hello");
// Type with custom delay
await user.type(input, " World", { delay: 50 });
expect(handleTextChange).toHaveBeenLastCalledWith("Hello World");
// Clear input
await user.clear(input);
expect(handleTextChange).toHaveBeenLastCalledWith("");
// Type after clearing
await user.type(input, "New text", { clearFirst: true });
expect(handleTextChange).toHaveBeenLastCalledWith("New text");
// Paste text
await user.paste(input, "\nPasted line");
expect(handleTextChange).toHaveBeenLastCalledWith("New text\nPasted line");
});
test("text input with validation", async () => {
const user = userEvent.setup();
render(
<TextInput
testID="disabled-input"
editable={false}
placeholder="Disabled input"
/>
);
const disabledInput = screen.getByTestId("disabled-input");
// This will throw an error because input is not editable
await expect(user.type(disabledInput, "text")).rejects.toThrow();
// Skip validation to force typing
await user.type(disabledInput, "forced text", { skipValidation: true });
});Realistic scrolling simulation for ScrollView and other scrollable components.
/**
* Scroll an element to a specific position
* @param element - ScrollView or scrollable element
* @param options - Scroll target options
* @returns Promise that resolves when scrolling is complete
*/
function scrollTo(element: ReactTestInstance, options: ScrollToOptions): Promise<void>;
interface ScrollToOptions {
/** Horizontal scroll position */
x?: number;
/** Vertical scroll position */
y?: number;
/** Whether to animate the scroll */
animated?: boolean;
}Usage Examples:
test("scroll interactions", async () => {
const user = userEvent.setup();
const handleScroll = jest.fn();
const handleMomentumScrollEnd = jest.fn();
render(
<ScrollView
testID="scroll-view"
onScroll={handleScroll}
onMomentumScrollEnd={handleMomentumScrollEnd}
scrollEventThrottle={16}
>
<View style={{ height: 2000 }}>
<Text>Long scrollable content</Text>
</View>
</ScrollView>
);
const scrollView = screen.getByTestId("scroll-view");
// Scroll vertically
await user.scrollTo(scrollView, { y: 500 });
expect(handleScroll).toHaveBeenCalled();
// Scroll horizontally
await user.scrollTo(scrollView, { x: 200 });
// Scroll to specific position with animation
await user.scrollTo(scrollView, {
x: 100,
y: 300,
animated: true
});
// Scroll back to top
await user.scrollTo(scrollView, { x: 0, y: 0 });
});Utilities for validating element states and event capabilities.
/**
* Check if element is a touch responder
* @param element - Element to check
* @returns True if element can respond to touch events
*/
function isTouchResponder(element: ReactTestInstance): boolean;
/**
* Check if event is enabled for element
* @param element - Element to check
* @param eventName - Event name to validate
* @returns True if event can be fired on element
*/
function isEventEnabled(
element: ReactTestInstance,
eventName: string
): boolean;Usage Examples:
import { render, screen, isTouchResponder, isEventEnabled } from "@testing-library/react-native";
test("event validation utilities", () => {
render(
<View>
<Pressable testID="pressable">
<Text>Pressable</Text>
</Pressable>
<View testID="plain-view">
<Text>Plain View</Text>
</View>
<TextInput testID="input" editable={false} />
<View pointerEvents="none" testID="no-pointer-events">
<Text>No Pointer Events</Text>
</View>
</View>
);
const pressable = screen.getByTestId("pressable");
const plainView = screen.getByTestId("plain-view");
const input = screen.getByTestId("input");
const noPointerView = screen.getByTestId("no-pointer-events");
// Check touch responder capability
expect(isTouchResponder(pressable)).toBe(true);
expect(isTouchResponder(plainView)).toBe(false);
expect(isTouchResponder(input)).toBe(true); // TextInput is always touch responder
// Check specific event capabilities
expect(isEventEnabled(pressable, "press")).toBe(true);
expect(isEventEnabled(noPointerView, "press")).toBe(false);
expect(isEventEnabled(input, "changeText")).toBe(false); // Not editable
});Global configuration for user event behavior and timing.
/**
* Create configured user event instance
* @param config - User event configuration options
* @returns Configured user event instance
*/
function setup(config?: UserEventConfig): UserEventInstance;
interface UserEventConfig {
/** Default delay between keystrokes when typing (ms) */
delay?: number;
/** Whether to skip element state validation by default */
skipValidation?: boolean;
/** Default duration for long press interactions (ms) */
longPressDelay?: number;
/** Whether to advance timers automatically in fake timer environments */
advanceTimers?: boolean;
}Usage Examples:
test("user event configuration", async () => {
// Create instance with custom configuration
const fastUser = userEvent.setup({
delay: 5, // Fast typing
longPressDelay: 200 // Short long press
});
const slowUser = userEvent.setup({
delay: 100, // Slow typing
longPressDelay: 1000 // Long press
});
const permissiveUser = userEvent.setup({
skipValidation: true // Skip all validation
});
render(<TextInput testID="input" placeholder="Type here" />);
const input = screen.getByTestId("input");
// Fast typing
await fastUser.type(input, "Fast text");
// Slow typing
await slowUser.type(input, "Slow text", { clearFirst: true });
// Type in disabled input (normally would throw)
render(<TextInput testID="disabled" editable={false} />);
const disabled = screen.getByTestId("disabled");
await permissiveUser.type(disabled, "Force type");
});Install with Tessl CLI
npx tessl i tessl/npm-testing-library--react-native