WellNuo/jest.setup.js
Sergei 69c999729f Fix BLE connections not disconnecting on logout
Implemented proper BLE cleanup mechanism on user logout:

**Root Cause:**
- BLE cleanup callback was being set but reference could become stale
- No explicit cleanup call in profile logout handler
- Callback stability issues due to re-renders

**Changes:**
1. app/_layout.tsx:
   - Use useRef pattern to maintain stable callback reference
   - Set callback once with ref that always points to current cleanupBLE
   - Cleanup callback on unmount to prevent memory leaks

2. app/(tabs)/profile/index.tsx:
   - Add explicit cleanupBLE() call in logout handler
   - Import useBLE hook to access cleanup function
   - Ensure cleanup happens before logout completes

3. services/api.ts:
   - Update setOnLogoutBLECleanupCallback signature to accept null
   - Allows proper cleanup of callback on unmount

4. jest.setup.js:
   - Add AsyncStorage mock to prevent test failures

5. Tests:
   - Add comprehensive BLE cleanup tests
   - Test callback pattern and stability
   - Test logout flow with BLE cleanup
   - Test error handling during cleanup

**Result:**
BLE connections now properly disconnect when user logs out,
preventing stale connections and potential resource leaks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 12:19:46 -08:00

68 lines
1.6 KiB
JavaScript

// 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
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-image-picker', () => ({
requestMediaLibraryPermissionsAsync: jest.fn(() =>
Promise.resolve({ status: 'granted' })
),
launchImageLibraryAsync: jest.fn(() =>
Promise.resolve({
canceled: false,
assets: [{ uri: 'file://test-image.jpg' }],
})
),
}));
// 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 });
// Silence console warnings in tests
global.console = {
...console,
warn: jest.fn(),
error: jest.fn(),
};