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);
});