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