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.'
);