// Setup testing library // Note: extend-expect is automatically loaded by @testing-library/react-native // Mock expo modules jest.mock('expo', () => ({ // Add any expo mocks here if needed })); // Mock expo modules core to prevent winter runtime errors jest.mock('expo-modules-core', () => ({ NativeModulesProxy: {}, requireNativeViewManager: jest.fn(), requireNativeModule: jest.fn(), EventEmitter: class EventEmitter {}, UnavailabilityError: class UnavailabilityError extends Error {}, })); // Mock expo winter runtime global.__ExpoImportMetaRegistry = { register: jest.fn(), get: jest.fn(() => ({})), }; // Mock structuredClone if not available if (typeof global.structuredClone === 'undefined') { global.structuredClone = (obj) => JSON.parse(JSON.stringify(obj)); } // Mock Expo modules jest.mock('expo-router', () => ({ router: { push: jest.fn(), replace: jest.fn(), back: jest.fn(), setParams: jest.fn(), }, useLocalSearchParams: jest.fn(() => ({})), useRouter: jest.fn(() => ({ push: jest.fn(), replace: jest.fn(), back: jest.fn(), })), useSegments: jest.fn(() => []), usePathname: jest.fn(() => '/'), })); jest.mock('expo-secure-store', () => ({ getItemAsync: jest.fn(), setItemAsync: jest.fn(), deleteItemAsync: jest.fn(), })); jest.mock('expo-crypto', () => { return { getRandomBytesAsync: jest.fn((size) => { const bytes = new Uint8Array(size); for (let i = 0; i < size; i++) { bytes[i] = (i * 7 + 13) % 256; } return Promise.resolve(bytes); }), digestStringAsync: jest.fn((algorithm, data) => { const hash = Buffer.from(data).toString('base64').substring(0, 64).padEnd(64, '0'); return Promise.resolve(hash); }), CryptoDigestAlgorithm: { SHA256: 'SHA256', }, }; }, { virtual: true }); jest.mock('expo-image-picker', () => ({ requestMediaLibraryPermissionsAsync: jest.fn(() => Promise.resolve({ status: 'granted' }) ), launchImageLibraryAsync: jest.fn(() => Promise.resolve({ canceled: false, assets: [{ uri: 'file://test-image.jpg' }], }) ), })); jest.mock('expo-image-manipulator', () => ({ manipulateAsync: jest.fn((uri, actions, options) => Promise.resolve({ uri: uri }) ), SaveFormat: { JPEG: 'jpeg', PNG: 'png', }, })); // Mock AsyncStorage jest.mock('@react-native-async-storage/async-storage', () => ({ getItem: jest.fn(), setItem: jest.fn(), removeItem: jest.fn(), clear: jest.fn(), getAllKeys: jest.fn(() => Promise.resolve([])), multiGet: jest.fn(() => Promise.resolve([])), multiSet: jest.fn(() => Promise.resolve()), multiRemove: jest.fn(() => Promise.resolve()), })); // Mock native modules jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper', () => ({ default: {}, }), { virtual: true }); // Mock Platform jest.mock('react-native/Libraries/Utilities/Platform', () => ({ OS: 'ios', select: jest.fn((obj) => obj.ios), Version: 14, })); // Mock PermissionsAndroid jest.mock('react-native/Libraries/PermissionsAndroid/PermissionsAndroid', () => ({ PERMISSIONS: { BLUETOOTH_SCAN: 'android.permission.BLUETOOTH_SCAN', BLUETOOTH_CONNECT: 'android.permission.BLUETOOTH_CONNECT', ACCESS_FINE_LOCATION: 'android.permission.ACCESS_FINE_LOCATION', }, RESULTS: { GRANTED: 'granted', DENIED: 'denied', NEVER_ASK_AGAIN: 'never_ask_again', }, request: jest.fn(() => Promise.resolve('granted')), requestMultiple: jest.fn(() => Promise.resolve({ 'android.permission.BLUETOOTH_SCAN': 'granted', 'android.permission.BLUETOOTH_CONNECT': 'granted', 'android.permission.ACCESS_FINE_LOCATION': 'granted', })), })); // Mock Linking jest.mock('react-native/Libraries/Linking/Linking', () => ({ openURL: jest.fn(() => Promise.resolve()), openSettings: jest.fn(() => Promise.resolve()), sendIntent: jest.fn(() => Promise.resolve()), })); // Mock Alert jest.mock('react-native/Libraries/Alert/Alert', () => ({ alert: jest.fn(), })); // Mock react-native-ble-plx jest.mock('react-native-ble-plx', () => ({ BleManager: jest.fn().mockImplementation(() => ({ startDeviceScan: jest.fn(), stopDeviceScan: jest.fn(), connectToDevice: jest.fn(), state: jest.fn(() => Promise.resolve('PoweredOn')), })), State: { Unknown: 'Unknown', Resetting: 'Resetting', Unsupported: 'Unsupported', Unauthorized: 'Unauthorized', PoweredOff: 'PoweredOff', PoweredOn: 'PoweredOn', }, BleError: class BleError extends Error {}, BleErrorCode: {}, })); // Mock react-native-base64 jest.mock('react-native-base64', () => ({ encode: jest.fn((str) => Buffer.from(str).toString('base64')), decode: jest.fn((str) => Buffer.from(str, 'base64').toString()), })); // Silence console warnings in tests global.console = { ...console, warn: jest.fn(), error: jest.fn(), };