diff --git a/packages/react-native-gesture-handler/jestSetup.js b/packages/react-native-gesture-handler/jestSetup.js index ebc6cd1a56..0a2d18c788 100644 --- a/packages/react-native-gesture-handler/jestSetup.js +++ b/packages/react-native-gesture-handler/jestSetup.js @@ -1,26 +1,38 @@ -jest.mock('./src/RNGestureHandlerModule', () => require('./src/mocks/mocks')); -jest.mock('./src/components/GestureButtons', () => require('./src/mocks/mocks')); -jest.mock('./src/components/Pressable/Pressable', () => require('./src/mocks/Pressable')); +jest.mock('./src/RNGestureHandlerModule', () => require('./src/mocks/module')); +jest.mock('./src/components/GestureButtons', () => require('./src/mocks/gestureButtons')); +jest.mock('./src/components/Pressable/Pressable', () => require('./src/mocks/Pressable')); +jest.mock('./src/components/GestureComponents', () => require('./src/mocks/gestureComponents')); +jest.mock('./src/v3/detectors/HostGestureDetector', () => require('./src/mocks/hostDetector')); jest.mock('./lib/commonjs/RNGestureHandlerModule', () => - require('./lib/commonjs/mocks/mocks') + require('./lib/commonjs/mocks/module') ); jest.mock('./lib/commonjs/components/GestureButtons', () => - require('./lib/commonjs/mocks/mocks') + require('./lib/commonjs/mocks/GestureButtons') ); jest.mock('./lib/commonjs/components/Pressable', () => require('./lib/commonjs/mocks/Pressable') ); - +jest.mock('./lib/commonjs/components/GestureComponents', () => + require('./lib/commonjs/mocks/gestureComponents') +); +jest.mock('./lib/commonjs/v3/detectors/HostGestureDetector', () => + require('./lib/commonjs/mocks/hostDetector') +); jest.mock('./lib/module/RNGestureHandlerModule', () => - require('./lib/module/mocks/mocks') + require('./lib/module/mocks/module') ); jest.mock('./lib/module/components/GestureButtons', () => - require('./lib/module/mocks/mocks') + require('./lib/module/mocks/GestureButtons') ); jest.mock('./lib/module/components/Pressable', () => require('./lib/module/mocks/Pressable') ); - +jest.mock('./lib/module/components/GestureComponents', () => + require('./lib/module/mocks/gestureComponents') +); +jest.mock('./lib/module/v3/detectors/HostGestureDetector', () => + require('./lib/module/mocks/hostDetector') +); diff --git a/packages/react-native-gesture-handler/src/__mocks__/RNGestureHandlerModule.ts b/packages/react-native-gesture-handler/src/__mocks__/RNGestureHandlerModule.ts index 1d52ce8df9..6c8c202a3a 100644 --- a/packages/react-native-gesture-handler/src/__mocks__/RNGestureHandlerModule.ts +++ b/packages/react-native-gesture-handler/src/__mocks__/RNGestureHandlerModule.ts @@ -1,4 +1,4 @@ -import Mocks from '../mocks/mocks'; +import Mocks from '../mocks/module'; export default { ...Mocks, diff --git a/packages/react-native-gesture-handler/src/__tests__/Errors.test.tsx b/packages/react-native-gesture-handler/src/__tests__/Errors.test.tsx new file mode 100644 index 0000000000..e494f2cf7a --- /dev/null +++ b/packages/react-native-gesture-handler/src/__tests__/Errors.test.tsx @@ -0,0 +1,125 @@ +import React from 'react'; +import { render, cleanup } from '@testing-library/react-native'; +import { + Gesture, + GestureDetector, + GestureHandlerRootView, + InterceptingGestureDetector, + useTapGesture, +} from '../index'; +import { findNodeHandle, View } from 'react-native'; +import { VirtualDetector } from '../v3/detectors/VirtualDetector/VirtualDetector'; + +beforeEach(() => cleanup()); +jest.mock('react-native/Libraries/ReactNative/RendererProxy', () => ({ + findNodeHandle: jest.fn(), +})); + +describe('VirtualDetector', () => { + test('virtual detector must be under InterceptingGestureDetector', () => { + function VirtualDetectorWithNoBoundary() { + const tap = useTapGesture({}); + return ( + + + + + + ); + } + + expect(() => render()).toThrow( + 'VirtualGestureDetector must be a descendant of an InterceptingGestureDetector' + ); + }); + test('virtual detector does not handle animated events', () => { + (findNodeHandle as jest.Mock).mockReturnValue(123); + + function VirtualDetectorAnimated() { + const tap = useTapGesture({ useAnimated: true }); + return ( + + + + + + + + ); + } + + expect(() => render()).toThrow( + 'VirtualGestureDetector cannot handle Animated events with native driver when used inside InterceptingGestureDetector. Use Reanimated or Animated events without native driver instead.' + ); + }); + + test('intercepting detector cant handle multiple types of events', () => { + (findNodeHandle as jest.Mock).mockReturnValue(123); + const mockWorklet = () => undefined; + mockWorklet.__workletHash = 123; + + function InterceptingDetectorMultipleTypes() { + const tap = useTapGesture({ useAnimated: true }); + const tap2 = useTapGesture({ onActivate: mockWorklet }); + return ( + + + + + + + + ); + } + + expect(() => render()).toThrow( + 'InterceptingGestureDetector can only handle either Reanimated or Animated events.' + ); + }); +}); + +describe('Check if descendant of root view', () => { + test('gesture detector', () => { + function GestureDetectorNoRootView() { + const tap = useTapGesture({}); + return ( + + + + ); + } + expect(() => render()).toThrow( + 'GestureDetector must be used as a descendant of GestureHandlerRootView. Otherwise the gestures will not be recognized. See https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation for more details.' + ); + }); + + test('intercepting detector', () => { + function GestureDetectorNoRootView() { + const tap = useTapGesture({}); + return ( + + + + ); + } + + expect(() => render()).toThrow( + 'GestureDetector must be used as a descendant of GestureHandlerRootView. Otherwise the gestures will not be recognized. See https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation for more details.' + ); + }); + + test('legacy detector', () => { + function GestureDetectorNoRootView() { + const tap = Gesture.Tap(); + return ( + + + + ); + } + + expect(() => render()).toThrow( + 'GestureDetector must be used as a descendant of GestureHandlerRootView. Otherwise the gestures will not be recognized. See https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation for more details.' + ); + }); +}); diff --git a/packages/react-native-gesture-handler/src/mocks/GestureButtons.tsx b/packages/react-native-gesture-handler/src/mocks/GestureButtons.tsx new file mode 100644 index 0000000000..a4ca02ae26 --- /dev/null +++ b/packages/react-native-gesture-handler/src/mocks/GestureButtons.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { TouchableNativeFeedback, View } from 'react-native'; +export const RawButton = ({ enabled, ...rest }: any) => ( + + + +); +export const BaseButton = RawButton; +export const RectButton = RawButton; +export const BorderlessButton = TouchableNativeFeedback; diff --git a/packages/react-native-gesture-handler/src/mocks/gestureComponents.tsx b/packages/react-native-gesture-handler/src/mocks/gestureComponents.tsx new file mode 100644 index 0000000000..a9270fc52e --- /dev/null +++ b/packages/react-native-gesture-handler/src/mocks/gestureComponents.tsx @@ -0,0 +1,23 @@ +import { + TouchableHighlight, + TouchableNativeFeedback, + TouchableOpacity, + TouchableWithoutFeedback, + ScrollView, + FlatList, + Switch, + TextInput, + DrawerLayoutAndroid, +} from 'react-native'; + +export default { + TouchableHighlight, + TouchableNativeFeedback, + TouchableOpacity, + TouchableWithoutFeedback, + ScrollView, + FlatList, + Switch, + TextInput, + DrawerLayoutAndroid, +} as const; diff --git a/packages/react-native-gesture-handler/src/mocks/hostDetector.tsx b/packages/react-native-gesture-handler/src/mocks/hostDetector.tsx new file mode 100644 index 0000000000..2739c45e0a --- /dev/null +++ b/packages/react-native-gesture-handler/src/mocks/hostDetector.tsx @@ -0,0 +1,5 @@ +import { View } from 'react-native'; + +const HostGestureDetector = View; + +export default HostGestureDetector; diff --git a/packages/react-native-gesture-handler/src/mocks/mocks.tsx b/packages/react-native-gesture-handler/src/mocks/mocks.tsx deleted file mode 100644 index e04a070782..0000000000 --- a/packages/react-native-gesture-handler/src/mocks/mocks.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { - TouchableHighlight, - TouchableNativeFeedback, - TouchableOpacity, - TouchableWithoutFeedback, - ScrollView, - FlatList, - Switch, - TextInput, - DrawerLayoutAndroid, - View, -} from 'react-native'; -import { State } from '../State'; -import { Directions } from '../Directions'; - -const NOOP = () => { - // Do nothing -}; -const PanGestureHandler = View; -const attachGestureHandler = NOOP; -const createGestureHandler = NOOP; -const dropGestureHandler = NOOP; -const setGestureHandlerConfig = NOOP; -const updateGestureHandlerConfig = NOOP; -const flushOperations = NOOP; -const configureRelations = NOOP; -const setReanimatedAvailable = NOOP; -const install = NOOP; -const NativeViewGestureHandler = View; -const TapGestureHandler = View; -const ForceTouchGestureHandler = View; -const LongPressGestureHandler = View; -const PinchGestureHandler = View; -const RotationGestureHandler = View; -const FlingGestureHandler = View; -export const RawButton = ({ enabled, ...rest }: any) => ( - - - -); -export const BaseButton = RawButton; -export const RectButton = RawButton; -export const BorderlessButton = TouchableNativeFeedback; - -export default { - TouchableHighlight, - TouchableNativeFeedback, - TouchableOpacity, - TouchableWithoutFeedback, - ScrollView, - FlatList, - Switch, - TextInput, - DrawerLayoutAndroid, - NativeViewGestureHandler, - TapGestureHandler, - ForceTouchGestureHandler, - LongPressGestureHandler, - PinchGestureHandler, - RotationGestureHandler, - FlingGestureHandler, - PanGestureHandler, - attachGestureHandler, - createGestureHandler, - dropGestureHandler, - setGestureHandlerConfig, - updateGestureHandlerConfig, - configureRelations, - setReanimatedAvailable, - flushOperations, - install, - // Probably can be removed - Directions, - State, -} as const; diff --git a/packages/react-native-gesture-handler/src/mocks/module.tsx b/packages/react-native-gesture-handler/src/mocks/module.tsx new file mode 100644 index 0000000000..c6b957e1d0 --- /dev/null +++ b/packages/react-native-gesture-handler/src/mocks/module.tsx @@ -0,0 +1,25 @@ +const NOOP = () => { + // Do nothing +}; + +const attachGestureHandler = NOOP; +const createGestureHandler = NOOP; +const dropGestureHandler = NOOP; +const setGestureHandlerConfig = NOOP; +const updateGestureHandlerConfig = NOOP; +const flushOperations = NOOP; +const configureRelations = NOOP; +const setReanimatedAvailable = NOOP; +const install = NOOP; + +export default { + attachGestureHandler, + createGestureHandler, + dropGestureHandler, + setGestureHandlerConfig, + updateGestureHandlerConfig, + configureRelations, + setReanimatedAvailable, + flushOperations, + install, +} as const; diff --git a/packages/react-native-gesture-handler/src/v3/detectors/useEnsureGestureHandlerRootView.ts b/packages/react-native-gesture-handler/src/v3/detectors/useEnsureGestureHandlerRootView.ts index 2cb5949bbd..41fdf97698 100644 --- a/packages/react-native-gesture-handler/src/v3/detectors/useEnsureGestureHandlerRootView.ts +++ b/packages/react-native-gesture-handler/src/v3/detectors/useEnsureGestureHandlerRootView.ts @@ -1,12 +1,11 @@ import { use } from 'react'; -import { isTestEnv } from '../../utils'; import { Platform } from 'react-native'; import GestureHandlerRootViewContext from '../../GestureHandlerRootViewContext'; export function useEnsureGestureHandlerRootView() { const rootViewContext = use(GestureHandlerRootViewContext); - if (__DEV__ && !rootViewContext && !isTestEnv() && Platform.OS !== 'web') { + if (__DEV__ && !rootViewContext && Platform.OS !== 'web') { throw new Error( 'GestureDetector must be used as a descendant of GestureHandlerRootView. Otherwise the gestures will not be recognized. See https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation for more details.' );