-
-
Notifications
You must be signed in to change notification settings - Fork 992
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
Support for useReanimatedGestureHandler in Jest #1762
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