-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Support for useReanimatedGestureHandler in Jest #1762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| ); | ||
| }; | ||
|
|
||
| const sendProgressEventFirst: SendEventWrapper = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const sendProgressEventFirst: SendEventWrapper = ( | |
| const sendActivateEvent: SendEventWrapper = ( |
Since it's sending a StateChangeEvent I would put the state in the method name like it is with other states.
src/jestUtils.ts
Outdated
| if (eventData?.onFail) { | ||
| states = { oldState: 2, state: 1 }; | ||
| } else if (eventData?.onCancel) { | ||
| states = { oldState: 2, state: 2 }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| states = { oldState: 2, state: 2 }; | |
| states = { oldState: 2, state: 3 }; |
Is it a typo, or is there a reason for it?
src/jestUtils.ts
Outdated
| handlers.forEach((handler) => { | ||
| sendEndEvent(handler, baseEventData, eventData?.configEnd); | ||
| }); | ||
|
|
||
| handlers.forEach((handler) => { | ||
| sendFailEvent(handler, baseEventData, eventData?.onFail); | ||
| }); | ||
|
|
||
| handlers.forEach((handler) => { | ||
| sendCancelEvent(handler, baseEventData, eventData?.onCancel); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only one of those should be sent at a time - gesture cannot end and fail or be cancelled at the same time. (END state means that the gesture finished because the user stopped interaction, i.e. the gesture finished "successfully").
src/jestUtils.ts
Outdated
| const config: HandlerProperties[] = | ||
| component?._fiber?.stateNode?.props?.ghTagContainer; | ||
| if (config) { | ||
| return config.filter((handler) => handler.handlerType === handlerType); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this approach will fail when there are two handlers of the same type attached to the same view, then there is no way to send an event to only one of them.
This has a use case for detecting a single and double tap on a view, or a fling with separate handlers for each direction.
src/jestUtils.ts
Outdated
|
|
||
| export const fireFlingGestureHandler = ( | ||
| component: any, | ||
| eventData?: FireGestureHandlerConfig<RotationConfig> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| eventData?: FireGestureHandlerConfig<RotationConfig> | |
| eventData?: FireGestureHandlerConfig<TapConfig> |
I'm not sure if sharing a config type between Tap and Fling is a good idea even though they have exactly the same properties. It may be confusing when looking at the API.
src/jestUtils.ts
Outdated
|
|
||
| export const firePinchGestureHandler = ( | ||
| component: any, | ||
| eventData?: FireGestureHandlerConfig<RotationConfig> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| eventData?: FireGestureHandlerConfig<RotationConfig> | |
| eventData?: FireGestureHandlerConfig<PinchConfig> |
src/jestUtils.ts
Outdated
| handlers.forEach((handler) => { | ||
| sendFinishEvent(handler, baseEventData, eventData); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to handle onFinish separately. useAnimatedGestureHandler implements a state machine that calls it when it receives a StateChangeEvent where oldState is either ACTIVE or BEGAN, and the state is not (so it must be END, FAILED, CANCELLED or UNDETERMINED).
| }); | ||
| const handlerProperties = { | ||
| handlerType: handler.handlerName, | ||
| handlerTag: handler.handlerTag, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a potential problem, as handlerTag is initialized in the attachHandlers method which is called in the useEffect hook. This has a value of -1 here.
| onStart: handlers.onBegin, | ||
| onActive: handlers.onUpdate, | ||
| onEnd: handlers.onEnd, | ||
| onFinish: handlers.onFinalize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| onStart: handlers.onBegin, | |
| onActive: handlers.onUpdate, | |
| onEnd: handlers.onEnd, | |
| onFinish: handlers.onFinalize, | |
| // this one passes the context to the onBegin callback, but I think it's fine in tests | |
| onStart: handlers.onBegin, | |
| onActive: (event, context) => { | |
| // useAnimatedGestureHandler passes all events with state === 4 (ACTIVE) to the onActive | |
| // callback, event the StateChangeEvent from 2 (BEGAN) to 4. Since the StateChangeEvent | |
| // will always be the first in the new state we can just check a flag here. | |
| if (context.active) { | |
| handlers.onUpdate?.(event); | |
| } else { | |
| handlers.onStart?.(event); | |
| context.active = true; | |
| } | |
| }, | |
| onEnd: (event, context) => { | |
| // in the new API onEnd, onFail and onCancel callbacks are merged into one, because | |
| // - from the user's perspective there is no meaningfull difference between gesture failing | |
| // and being cancelled, so having separate callbacks for it makes no sense | |
| // - most of the time you want to clean up after the gesture in all of those callbacks, | |
| // so there is no point in repeating the same logic three times | |
| // new onEnd callback receives a bool as the second argument which tells whether the gesture | |
| // has finished gracefully or not, and if you really need to know if it failed or was cancelled | |
| // you can chech the state property of event | |
| handlers.onEnd?.(event, true); | |
| context.success = true; | |
| }, | |
| onCancel: (event, context) => { | |
| handlers.onEnd?.(event, false); | |
| context.success = false; | |
| }, | |
| onFail: (event, context) => { | |
| handlers.onEnd?.(event, false); | |
| context.success = false; | |
| }, | |
| onFinish: (event, context) => { | |
| // onFinalize, like onEnd, requires a flag that tells whether the gesture finished gracefully | |
| handlers.onFinalize?.(event, context.success ?? false); | |
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if emulating the new API using the old one is the best approach.
src/jestUtils.ts
Outdated
| if (Array.isArray(component.props.children)) { | ||
| for (const child of component.props.children) { | ||
| decorateChildrenWithTag(child, handlerProperties); | ||
| } | ||
| } else if (typeof component.props.children === 'object') { | ||
| decorateChildrenWithTag(component.props.children, handlerProperties); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this may fail if you were to use your own component as a child of GestureDetector, then its inner structure wouldn't be decorated. In case of gesture handlers it shouldn't matter that much since they required a native view as a direct child.
|
Superseded by #1844. |
Description
I want to add support for useReanimatedGestureHandler in Jest tests and I want to make it user-friendly. So I added configuration for events, ready to use in Jest.
Related PR: software-mansion/react-native-reanimated#2747
Test plan
Example usage