/** * Jest Setup for Mobile App */ // Mock expo-secure-store jest.mock('expo-secure-store', () => ({ getItemAsync: jest.fn(() => Promise.resolve(null)), setItemAsync: jest.fn(() => Promise.resolve()), deleteItemAsync: jest.fn(() => Promise.resolve()), })); // Mock @react-native-async-storage/async-storage jest.mock('@react-native-async-storage/async-storage', () => ({ getItem: jest.fn(() => Promise.resolve(null)), setItem: jest.fn(() => Promise.resolve()), removeItem: jest.fn(() => Promise.resolve()), getAllKeys: jest.fn(() => Promise.resolve([])), multiRemove: jest.fn(() => Promise.resolve()), })); // Mock @react-native-community/netinfo jest.mock('@react-native-community/netinfo', () => ({ addEventListener: jest.fn(() => jest.fn()), fetch: jest.fn(() => Promise.resolve({ isConnected: true, isInternetReachable: true, }) ), })); // Mock expo-notifications jest.mock('expo-notifications', () => ({ setNotificationHandler: jest.fn(), getPermissionsAsync: jest.fn(() => Promise.resolve({ status: 'granted', canAskAgain: true }) ), requestPermissionsAsync: jest.fn(() => Promise.resolve({ status: 'granted' }) ), getExpoPushTokenAsync: jest.fn(() => Promise.resolve({ data: 'ExponentPushToken[test]' }) ), setNotificationChannelAsync: jest.fn(() => Promise.resolve()), scheduleNotificationAsync: jest.fn(() => Promise.resolve('notification-id')), cancelScheduledNotificationAsync: jest.fn(() => Promise.resolve()), cancelAllScheduledNotificationsAsync: jest.fn(() => Promise.resolve()), getAllScheduledNotificationsAsync: jest.fn(() => Promise.resolve([])), dismissAllNotificationsAsync: jest.fn(() => Promise.resolve()), setBadgeCountAsync: jest.fn(() => Promise.resolve()), getBadgeCountAsync: jest.fn(() => Promise.resolve(0)), addNotificationReceivedListener: jest.fn(() => ({ remove: jest.fn() })), addNotificationResponseReceivedListener: jest.fn(() => ({ remove: jest.fn() })), getLastNotificationResponseAsync: jest.fn(() => Promise.resolve(null)), DEFAULT_ACTION_IDENTIFIER: 'expo.modules.notifications.actions.DEFAULT', AndroidImportance: { MAX: 5, HIGH: 4, DEFAULT: 3, LOW: 2, MIN: 1, }, })); // Mock expo-local-authentication jest.mock('expo-local-authentication', () => ({ hasHardwareAsync: jest.fn(() => Promise.resolve(true)), isEnrolledAsync: jest.fn(() => Promise.resolve(true)), supportedAuthenticationTypesAsync: jest.fn(() => Promise.resolve([1])), getEnrolledLevelAsync: jest.fn(() => Promise.resolve(2)), authenticateAsync: jest.fn(() => Promise.resolve({ success: true }) ), AuthenticationType: { FINGERPRINT: 1, FACIAL_RECOGNITION: 2, IRIS: 3, }, SecurityLevel: { NONE: 0, SECRET: 1, BIOMETRIC: 2, }, })); // Mock expo-camera jest.mock('expo-camera', () => ({ Camera: { getCameraPermissionsAsync: jest.fn(() => Promise.resolve({ status: 'granted', canAskAgain: true }) ), requestCameraPermissionsAsync: jest.fn(() => Promise.resolve({ status: 'granted' }) ), }, CameraView: 'CameraView', CameraType: { back: 'back', front: 'front', }, })); // Mock expo-haptics jest.mock('expo-haptics', () => ({ impactAsync: jest.fn(() => Promise.resolve()), ImpactFeedbackStyle: { Light: 'light', Medium: 'medium', Heavy: 'heavy', }, })); // Mock expo-device jest.mock('expo-device', () => ({ isDevice: true, })); // Silence console warnings during tests global.console = { ...console, warn: jest.fn(), error: jest.fn(), }; // Add custom matchers if needed expect.extend({ toBeWithinRange(received, floor, ceiling) { const pass = received >= floor && received <= ceiling; if (pass) { return { message: () => `expected ${received} not to be within range ${floor} - ${ceiling}`, pass: true, }; } else { return { message: () => `expected ${received} to be within range ${floor} - ${ceiling}`, pass: false, }; } }, });