Skip to content
Open
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
116 changes: 114 additions & 2 deletions packages/docs-gesture-handler/docs/guides/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,119 @@ In order to load mocks provided by RNGH, add the following to your jest config:

## Testing Gestures' and Gesture handlers' callbacks

RNGH provides APIs, specifically [`fireGestureHandler`](#firegesturehandler) and [`getByGestureTestId`](#getbygesturetestid), to trigger selected handlers.
RNGH provides the following APIs for triggering selected handlers:

- [`createGestureController`](#creategesturecontroller) to imperatively control a gesture lifecycle one step at a time.
- [`fireGestureHandler`](#firegesturehandler) to dispatch a complete event stream.
- [`getByGestureTestId`](#getbygesturetestid) to find a gesture by its test ID.

### createGestureController

<CollapsibleCode
label="Show composed types definitions"
expandedLabel="Hide composed types definitions"
lineBounds={[0, 3]}
src={`
import { createGestureController } from 'react-native-gesture-handler/jest-utils';

createGestureController: (componentOrGesture) => GestureController;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does GestureController work with older APIs or only V3?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it should work with older APIs but not sure if we need to add test examples for them.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need I think

Comment thread
m-bert marked this conversation as resolved.

export interface GestureController<
TEventPayload extends Record<string, unknown> = Record<string, unknown>,
> {
begin: (event?: GestureControllerEvent<TEventPayload>) => void;
activate: (event?: GestureControllerEvent<TEventPayload>) => void;
update: (event?: GestureControllerEvent<TEventPayload>) => void;
end: (event?: GestureControllerEvent<TEventPayload>) => void;
fail: (event?: GestureControllerEvent<TEventPayload>) => void;
cancel: (event?: GestureControllerEvent<TEventPayload>) => void;
}

export type GestureControllerEvent<
TEventPayload extends Record<string, unknown> = Record<string, unknown>,
> = Partial<TEventPayload> & {
handlerTag?: never;
nativeEvent?: never;
oldState?: never;
state?: never;
};
`}/>

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:

| Method | Behavior |
| ------------ | ---------------------------------------------------------------------------------------------------------------- |
| `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.

```tsx
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();
Comment thread
j-piasecki marked this conversation as resolved.
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

Expand Down Expand Up @@ -133,7 +245,7 @@ components.
`testID` must be unique among components rendered in test.
:::

## Example
## fireGestureHandler example

Extracted from RNGH tests, check [`api_v3.test.tsx`](https://github.com/software-mansion/react-native-gesture-handler/blob/main/packages/react-native-gesture-handler/src/__tests__/api_v3.test.tsx) for full implementation.

Expand Down
Loading
Loading