- Add missing addEventListener/removeEventListener mocks in BLEContext cleanup tests - Fix scanning simulation by calling scanDevices() instead of trying to set state directly - Add explicit timeouts to bulkSetWiFi tests (10-15s) to accommodate MockBLEManager delays - Update concurrent connection error message check to match actual error text All 42 BLE tests now pass (36 bulk/concurrent + 6 cleanup)
196 lines
5.7 KiB
TypeScript
196 lines
5.7 KiB
TypeScript
/**
|
|
* BLE Context Cleanup Tests
|
|
* Tests for BLE scanning cleanup when screens lose focus
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { renderHook, act, waitFor } from '@testing-library/react-native';
|
|
import { BLEProvider, useBLE } from '@/contexts/BLEContext';
|
|
import * as bleModule from '@/services/ble';
|
|
|
|
// Mock the BLE service
|
|
jest.mock('@/services/ble', () => ({
|
|
bleManager: {
|
|
scanDevices: jest.fn(),
|
|
stopScan: jest.fn(),
|
|
connectDevice: jest.fn(),
|
|
disconnectDevice: jest.fn(),
|
|
getWiFiList: jest.fn(),
|
|
setWiFi: jest.fn(),
|
|
getCurrentWiFi: jest.fn(),
|
|
rebootDevice: jest.fn(),
|
|
cleanup: jest.fn(),
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
},
|
|
isBLEAvailable: true,
|
|
}));
|
|
|
|
describe('BLEContext cleanup behavior', () => {
|
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
<BLEProvider>{children}</BLEProvider>
|
|
);
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
it('should stop scan when cleanupBLE is called while scanning', async () => {
|
|
const mockScanDevices = jest.fn().mockResolvedValue([
|
|
{ id: 'device-1', name: 'WP_497_81a14c', mac: '142B2F81A14C', rssi: -55, wellId: 497 },
|
|
]);
|
|
const mockStopScan = jest.fn();
|
|
|
|
(bleModule.bleManager.scanDevices as jest.Mock) = mockScanDevices;
|
|
(bleModule.bleManager.stopScan as jest.Mock) = mockStopScan;
|
|
|
|
const { result } = renderHook(() => useBLE(), { wrapper });
|
|
|
|
// Start scanning
|
|
act(() => {
|
|
result.current.scanDevices();
|
|
});
|
|
|
|
expect(result.current.isScanning).toBe(true);
|
|
|
|
// Cleanup while scanning
|
|
await act(async () => {
|
|
await result.current.cleanupBLE();
|
|
});
|
|
|
|
expect(mockStopScan).toHaveBeenCalled();
|
|
expect(result.current.isScanning).toBe(false);
|
|
});
|
|
|
|
it('should clear found devices when cleanupBLE is called', async () => {
|
|
const mockScanDevices = jest.fn().mockResolvedValue([
|
|
{ id: 'device-1', name: 'WP_497_81a14c', mac: '142B2F81A14C', rssi: -55, wellId: 497 },
|
|
{ id: 'device-2', name: 'WP_498_82b25d', mac: '142B2F82B25D', rssi: -60, wellId: 498 },
|
|
]);
|
|
|
|
(bleModule.bleManager.scanDevices as jest.Mock) = mockScanDevices;
|
|
|
|
const { result } = renderHook(() => useBLE(), { wrapper });
|
|
|
|
// Scan and find devices
|
|
await act(async () => {
|
|
await result.current.scanDevices();
|
|
});
|
|
|
|
expect(result.current.foundDevices).toHaveLength(2);
|
|
|
|
// Cleanup
|
|
await act(async () => {
|
|
await result.current.cleanupBLE();
|
|
});
|
|
|
|
expect(result.current.foundDevices).toHaveLength(0);
|
|
});
|
|
|
|
it('should clear connected devices when cleanupBLE is called', async () => {
|
|
const mockConnectDevice = jest.fn().mockResolvedValue(true);
|
|
const mockCleanup = jest.fn().mockResolvedValue(undefined);
|
|
|
|
(bleModule.bleManager.connectDevice as jest.Mock) = mockConnectDevice;
|
|
(bleModule.bleManager.cleanup as jest.Mock) = mockCleanup;
|
|
|
|
const { result } = renderHook(() => useBLE(), { wrapper });
|
|
|
|
// Connect to a device
|
|
await act(async () => {
|
|
await result.current.connectDevice('device-1');
|
|
});
|
|
|
|
expect(result.current.connectedDevices.size).toBe(1);
|
|
|
|
// Cleanup
|
|
await act(async () => {
|
|
await result.current.cleanupBLE();
|
|
});
|
|
|
|
expect(mockCleanup).toHaveBeenCalled();
|
|
expect(result.current.connectedDevices.size).toBe(0);
|
|
});
|
|
|
|
it('should clear errors when cleanupBLE is called', async () => {
|
|
const mockScanDevices = jest.fn().mockRejectedValue(new Error('Scan failed'));
|
|
|
|
(bleModule.bleManager.scanDevices as jest.Mock) = mockScanDevices;
|
|
|
|
const { result } = renderHook(() => useBLE(), { wrapper });
|
|
|
|
// Trigger an error
|
|
await act(async () => {
|
|
try {
|
|
await result.current.scanDevices();
|
|
} catch {
|
|
// Expected
|
|
}
|
|
});
|
|
|
|
expect(result.current.error).toBeTruthy();
|
|
|
|
// Cleanup should clear errors
|
|
await act(async () => {
|
|
await result.current.cleanupBLE();
|
|
});
|
|
|
|
expect(result.current.error).toBeNull();
|
|
});
|
|
|
|
it('should not throw if cleanup fails', async () => {
|
|
const mockCleanup = jest.fn().mockRejectedValue(new Error('Cleanup failed'));
|
|
|
|
(bleModule.bleManager.cleanup as jest.Mock) = mockCleanup;
|
|
|
|
const { result } = renderHook(() => useBLE(), { wrapper });
|
|
|
|
// Cleanup should not throw even if bleManager.cleanup fails
|
|
await expect(
|
|
act(async () => {
|
|
await result.current.cleanupBLE();
|
|
})
|
|
).resolves.not.toThrow();
|
|
});
|
|
|
|
it('should stop scan before calling bleManager.cleanup', async () => {
|
|
const callOrder: string[] = [];
|
|
|
|
// Make scanDevices take a long time so we can cleanup while it's scanning
|
|
const mockScanDevices = jest.fn().mockImplementation(() => {
|
|
return new Promise((resolve) => {
|
|
// Simulate a long-running scan
|
|
setTimeout(() => resolve([]), 5000);
|
|
});
|
|
});
|
|
const mockStopScan = jest.fn(() => {
|
|
callOrder.push('stopScan');
|
|
});
|
|
const mockCleanup = jest.fn().mockResolvedValue(undefined).mockImplementation(() => {
|
|
callOrder.push('cleanup');
|
|
});
|
|
|
|
(bleModule.bleManager.scanDevices as jest.Mock) = mockScanDevices;
|
|
(bleModule.bleManager.stopScan as jest.Mock) = mockStopScan;
|
|
(bleModule.bleManager.cleanup as jest.Mock) = mockCleanup;
|
|
|
|
const { result } = renderHook(() => useBLE(), { wrapper });
|
|
|
|
// Start scanning (don't await - let it run in background)
|
|
act(() => {
|
|
result.current.scanDevices();
|
|
});
|
|
|
|
// isScanning should be true now
|
|
expect(result.current.isScanning).toBe(true);
|
|
|
|
// Trigger cleanup while scanning
|
|
await act(async () => {
|
|
await result.current.cleanupBLE();
|
|
});
|
|
|
|
// stopScan should be called before cleanup
|
|
expect(callOrder).toEqual(['stopScan', 'cleanup']);
|
|
});
|
|
});
|