diff --git a/packages/react-native-gesture-handler/jestSetup.js b/packages/react-native-gesture-handler/jestSetup.js index ebc6cd1a56..5ed8cc24fd 100644 --- a/packages/react-native-gesture-handler/jestSetup.js +++ b/packages/react-native-gesture-handler/jestSetup.js @@ -7,7 +7,7 @@ jest.mock('./lib/commonjs/RNGestureHandlerModule', () => require('./lib/commonjs/mocks/mocks') ); 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') @@ -18,7 +18,7 @@ jest.mock('./lib/module/RNGestureHandlerModule', () => require('./lib/module/mocks/mocks') ); 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') 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/mocks.tsx b/packages/react-native-gesture-handler/src/mocks/mocks.tsx index 03d42f34fc..fff6f2ca24 100644 --- a/packages/react-native-gesture-handler/src/mocks/mocks.tsx +++ b/packages/react-native-gesture-handler/src/mocks/mocks.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { TouchableHighlight, TouchableNativeFeedback, @@ -17,7 +16,7 @@ import { Directions } from '../Directions'; const NOOP = () => { // Do nothing }; -const PanGestureHandler = View; + const attachGestureHandler = NOOP; const createGestureHandler = NOOP; const dropGestureHandler = NOOP; @@ -26,6 +25,8 @@ const updateGestureHandlerConfig = NOOP; const flushOperations = NOOP; const configureRelations = NOOP; const install = NOOP; + +const PanGestureHandler = View; const NativeViewGestureHandler = View; const TapGestureHandler = View; const ForceTouchGestureHandler = View; @@ -33,14 +34,7 @@ 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; +const HostGestureDetector = View; export default { TouchableHighlight, @@ -68,6 +62,7 @@ export default { configureRelations, flushOperations, install, + HostGestureDetector, // Probably can be removed Directions, State, 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.' );