// Mock react-native BEFORE any other imports to avoid flow type issues jest.mock('react-native', () => { const React = require('react') return { RefreshControl: 'RefreshControl', ScrollView: 'ScrollView', FlatList: 'FlatList', View: 'View', Text: 'Text', Image: 'Image', Pressable: 'Pressable', TouchableOpacity: 'TouchableOpacity', TextInput: 'TextInput', Platform: { OS: 'web' }, Animated: { View: 'Animated.View', Text: 'Animated.Text', Image: 'Animated.Image', Value: class MockValue { constructor(v) { this._value = v } setValue(v) { this._value = v } __getValue() { return this._value } interpolate() { return this } }, timing: () => ({ start: jest.fn() }), spring: () => ({ start: jest.fn() }), event: () => jest.fn(), }, PanResponder: { create: () => ({ panHandlers: {} }), }, ActivityIndicator: 'ActivityIndicator', StyleSheet: { create: (styles) => styles }, Dimensions: { get: () => ({ width: 375, height: 812 }) }, StatusBar: 'StatusBar', SafeAreaView: 'SafeAreaView', useSafeAreaInsets: () => ({ top: 0, bottom: 0, left: 0, right: 0 }), NativeModules: { DevMenu: {}, SettingsManager: {}, }, Settings: { get: jest.fn(), set: jest.fn(), }, } }) require('@testing-library/jest-native/extend-expect') // Initialize globals for react-native-reanimated global._tagToJSPropNamesMapping = {} global._WORKLET = false global._ReanimatedModule = {} // Mock react-native modules // Note: NativeAnimatedHelper may not be needed in newer React Native versions // jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper') // Mock expo modules jest.mock('expo-constants', () => ({ default: {}, Constants: {}, })) jest.mock('expo-modules-core', () => ({ EventEmitter: class MockEventEmitter {}, requireNativeViewManager: jest.fn(() => ({})), requireOptionalNativeModule: jest.fn(() => ({})), requireNativeModule: jest.fn(() => ({})), })) // Mock expo-asset jest.mock('expo-asset', () => ({ AssetModule: {}, Asset: class MockAsset { static fromModule = jest.fn(() => ({ localUri: '' })) static fromURI = jest.fn(() => ({ localUri: '' })) }, })) // Mock expo-image jest.mock('expo-image', () => ({ Image: 'Image', })) // Mock expo-linear-gradient jest.mock('expo-linear-gradient', () => ({ LinearGradient: 'LinearGradient', })) // Mock react-native-gesture-handler jest.mock('react-native-gesture-handler', () => { const { View } = require('react-native') return { GestureDetector: View, Gesture: { Tap: () => ({}), Pan: () => ({}), LongPress: () => ({}), Pinch: () => ({}), Rotation: () => ({}), Fling: () => ({}), NativeViewGesture: () => ({}), ForceTouch: () => ({}), ManualGesture: () => ({}), }, GestureHandlerRootView: View, RawButton: View, BaseButton: View, RectButton: View, BorderlessButton: View, State: {}, Directions: {}, } }) jest.mock('expo-linking', () => ({ createURL: (url) => url, parse: (url) => ({ path: url }), })) jest.mock('expo-secure-store', () => ({ getItemAsync: jest.fn(), setItemAsync: jest.fn(), deleteItemAsync: jest.fn(), })) jest.mock('expo-font', () => ({ useFonts: () => [true, null], })) jest.mock('expo-splash-screen', () => ({ preventAutoHideAsync: jest.fn(), hideAsync: jest.fn(), })) // Mock react-native-reanimated jest.mock('react-native-reanimated', () => { const Reanimated = require('react-native-reanimated/mock') Reanimated.default.call = () => {} return Reanimated }) // Mock nativewind jest.mock('nativewind', () => ({ styled: jest.fn(), })) // Mock @react-navigation/native jest.mock('@react-navigation/native', () => ({ useNavigation: () => ({ navigate: jest.fn(), goBack: jest.fn(), reset: jest.fn(), }), useRoute: () => ({ params: {}, }), NavigationContainer: ({ children }) => children, })) // Mock expo-router jest.mock('expo-router', () => ({ useRouter: () => ({ push: jest.fn(), replace: jest.fn(), back: jest.fn(), }), useLocalSearchParams: () => ({}), useSegments: () => [], usePathname: () => '/', })) // Global mock for console methods to reduce noise in tests global.console = { ...console, error: jest.fn(), warn: jest.fn(), log: jest.fn(), } // Mock @repo/core and @repo/sdk jest.mock('@repo/core', () => ({ root: { get: jest.fn(), }, })) jest.mock('@repo/sdk', () => ({ CategoryController: class MockCategoryController {}, })) // Note: The second react-native mock has been removed to avoid circular dependency issues // The first mock at the top of this file handles all necessary react-native mocking // Mock DevMenu module jest.mock('react-native/src/private/devsupport/devmenu/DevMenu', () => ({ default: {}, })) // Mock SettingsManager module jest.mock('react-native/src/private/specs_DEPRECATED/modules/NativeSettingsManager', () => ({ default: { getConstants: () => ({}), }, })) // Mock lib/auth to avoid TypeScript compilation errors jest.mock('@/lib/auth', () => ({ OWNER_ID: 'test-owner-id', })) // Mock components jest.mock('@/components/icon', () => ({ DownArrowIcon: () => null, PointsIcon: () => null, SearchIcon: () => null, LeftArrowIcon: () => null, UploadIcon: () => null, WhitePointsIcon: () => null, })) jest.mock('@/components/skeleton/HomeSkeleton', () => ({ HomeSkeleton: () => null, })) // Mock drawer components jest.mock('@/components/drawer/UploadReferenceImageDrawer', () => ({ default: () => null, })) // Mock SearchResultsGrid jest.mock('@/components/SearchResultsGrid', () => ({ default: () => null, })) // Mock FlashList to actually render items jest.mock('@shopify/flash-list', () => { const { View } = require('react-native') return { FlashList: ({ data, renderItem }) => { return data.map((item, index) => renderItem({ item, index })) }, } }) // Mock @gorhom/bottom-sheet jest.mock('@gorhom/bottom-sheet', () => { const { View } = require('react-native') return { BottomSheet: View, BottomSheetView: View, BottomSheetModal: View, BottomSheetModalProvider: ({ children }) => children, BottomSheetScrollView: ({ children }) => children, useBottomSheet: () => ({ snapTo: jest.fn(), expand: jest.fn(), collapse: jest.fn(), close: jest.fn(), }), useBottomSheetModal: () => ({ present: jest.fn(), dismiss: jest.fn(), }), useBottomSheetSpringConfigs: jest.fn(() => ({})), useBottomSheetTimingConfigs: jest.fn(() => ({})), } })