Skip to main content
Version: 3.x

Testing with Jest

Setup

Jest configuration

In order to use functions provided by Gesture Handler, add react-native-gesture-handler to transformIgnorePatterns in jest config.

transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|react-native-gesture-handler)/)',
],
note

Be careful when adding multiple entries to transformIgnorePatterns. Since Jest ignores a file if it matches any pattern in the list, splitting negative lookaheads into separate strings often causes them to override each other. It is safer to combine your exceptions into a single regex using the | operator. See the Jest documentation for an example.

Mocking native modules

In order to load mocks provided by RNGH, add the following to your jest config:

"setupFiles": ["./node_modules/react-native-gesture-handler/jestSetup.js"]

Example jest config

module.exports = {
preset: '@react-native/jest-preset',
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|react-native-gesture-handler)/)',
],
setupFiles: ['react-native-gesture-handler/jestSetup.js'],
};

Testing Gestures' and Gesture handlers' callbacks

RNGH provides the following APIs for triggering selected handlers:

createGestureController


import { createGestureController } from 'react-native-gesture-handler/jest-utils';

createGestureController: (componentOrGesture) => GestureController;

Creates an imperative controller that dispatches gesture lifecycle events one step at a time. This allows the test to assert application state between lifecycle steps without manually supplying state, oldState, or handlerTag.

componentOrGesture can be:

  • A Gesture Handler component found using a Jest query such as getByTestId.
  • A gesture object.
  • A gesture test ID string.

When a gesture object is passed directly, the event payload type is inferred from the gesture. Every lifecycle method accepts an optional partial event payload and fills omitted handler-specific properties with defaults.

The controller exposes the following methods:

MethodBehavior
begin()Starts a stream and calls onBegin. If the previous stream finished, the controller resets it before beginning.
activate()Activates a begun stream and calls onActivate.
update()Dispatches an update for an active stream and calls onUpdate. It can be called multiple times.
end()Ends a begun or active stream. Calls onDeactivate if active, then onFinalize with canceled: false.
fail()Fails a begun or active stream. Calls onDeactivate if active, then onFinalize with canceled: true.
cancel()Cancels a begun or active stream. Calls onDeactivate if active, then onFinalize with canceled: true.

Calling begin() again after end(), fail(), or cancel() starts another stream with the same controller. Calling methods in an invalid order throws an error. State-machine fields such as state, oldState, handlerTag, and nativeEvent cannot be supplied in event payloads because the controller manages them internally.

test('updates application state after each gesture step', () => {
const onBegin = jest.fn();
const onActivate = jest.fn();
const onUpdate = jest.fn();
const onDeactivate = jest.fn();
const onFinalize = jest.fn();

const panGesture = renderHook(() =>
usePanGesture({
disableReanimated: true,
onBegin,
onActivate,
onUpdate,
onDeactivate,
onFinalize,
})
).result.current;

const controller = createGestureController(panGesture);

controller.begin();
expect(onBegin).toHaveBeenCalledTimes(1);

controller.activate();
expect(onActivate).toHaveBeenCalledTimes(1);

controller.update({ translationX: 50 });
expect(onUpdate).toHaveBeenCalledWith(
expect.objectContaining({ translationX: 50 })
);

controller.end();
expect(onDeactivate).toHaveBeenCalledTimes(1);
expect(onFinalize).toHaveBeenCalledWith(
expect.objectContaining({ canceled: false })
);
});
note

createGestureController controls lifecycle events directly. It does not generate pointer input, run platform gesture recognizers, or evaluate relations between gestures.

fireGestureHandler

import { fireGestureHandler } from 'react-native-gesture-handler/jest-utils';

fireGestureHandler: (componentOrGesture, eventList) => void;

Simulates one event stream (i.e. event sequence starting with BEGIN state and ending with one of END/FAIL/CANCEL states), calling appropriate callbacks associated with given gesture handler.

  • componentOrGesture - Either Gesture Handler component found by Jest queries (e.g. getByTestId) or Gesture found by getByGestureTestId()

  • eventList - Event data passed to appropriate callback. RNGH fills event list if required data is missing using these rules:

    • oldState is filled using state of the previous event. BEGIN events use UNDETERMINED value as previous event.
    • Events after first ACTIVE state can omit state field.
    • Handler specific data is filled (e.g. numberOfTouches, x fields) with defaults.
    • Missing BEGIN and END events are added with data copied from first and last passed event, respectively.
    • If first event doesn't have state field, the ACTIVE state is assumed.

Some eventList examples:

const oldStateFilled = [
{ state: State.BEGAN },
{ state: State.ACTIVE },
{ state: State.END },
]; // three events with specified state are fired.

const implicitActiveState = [
{ state: State.BEGAN },
{ state: State.ACTIVE },
{ x: 5 },
{ state: State.END },
]; // 4 events, including two ACTIVE events (second one has overridden additional data).

const implicitBegin = [
{ x: 1, y: 11 },
{ x: 2, y: 12, state: State.FAILED },
]; // 3 events, including implicit BEGAN, one ACTIVE, and one FAILED event with additional data.

const implicitBeginAndEnd = [
{ x: 5, y: 15 },
{ x: 6, y: 16 },
{ x: 7, y: 17 },
]; // 5 events, including 3 ACTIVE events and implicit BEGAN and END events. BEGAN uses first event's additional data, END uses last event's additional data.

const allImplicits = []; // 3 events, one BEGIN, one ACTIVE, one END with defaults.

getByGestureTestId

import { getByGestureTestId } from 'react-native-gesture-handler/jest-utils';

getByGestureTestId: (testID: string) => Gesture;

Returns opaque data type associated with gesture. Gesture is found via testID attribute in rendered components.

warning

testID must be unique among components rendered in test.

fireGestureHandler example

Extracted from RNGH tests, check api_v3.test.tsx for full implementation.

test('Pan gesture', () => {
const onBegin = jest.fn();
const onStart = jest.fn();

const panGesture = renderHook(() =>
usePanGesture({
disableReanimated: true,
onBegin: (e) => onBegin(e),
onActivate: (e) => onStart(e),
})
).result.current;

fireGestureHandler(panGesture, [
{ oldState: State.UNDETERMINED, state: State.BEGAN },
{ oldState: State.BEGAN, state: State.ACTIVE },
{ oldState: State.ACTIVE, state: State.ACTIVE },
{ oldState: State.ACTIVE, state: State.END },
]);

expect(onBegin).toHaveBeenCalledTimes(1);
expect(onStart).toHaveBeenCalledTimes(1);
});