Fix BLE test failures in cleanup, bulk, and concurrent tests

- 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)
This commit is contained in:
Sergei 2026-02-01 11:18:34 -08:00
parent c2064a76eb
commit 9ceb20c4fa
3 changed files with 36 additions and 19 deletions

View File

@ -20,6 +20,8 @@ jest.mock('@/services/ble', () => ({
getCurrentWiFi: jest.fn(),
rebootDevice: jest.fn(),
cleanup: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
},
isBLEAvailable: true,
}));
@ -35,7 +37,7 @@ describe('BLEContext cleanup behavior', () => {
it('should stop scan when cleanupBLE is called while scanning', async () => {
const mockScanDevices = jest.fn().mockResolvedValue([
{ id: 'device-1', name: 'WP_497_81a14c', mac: '81A14C', rssi: -55, wellId: 497 },
{ id: 'device-1', name: 'WP_497_81a14c', mac: '142B2F81A14C', rssi: -55, wellId: 497 },
]);
const mockStopScan = jest.fn();
@ -62,8 +64,8 @@ describe('BLEContext cleanup behavior', () => {
it('should clear found devices when cleanupBLE is called', async () => {
const mockScanDevices = jest.fn().mockResolvedValue([
{ id: 'device-1', name: 'WP_497_81a14c', mac: '81A14C', rssi: -55, wellId: 497 },
{ id: 'device-2', name: 'WP_498_82b25d', mac: '82B25D', rssi: -60, wellId: 498 },
{ 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;
@ -154,6 +156,13 @@ describe('BLEContext cleanup behavior', () => {
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');
});
@ -161,17 +170,21 @@ describe('BLEContext cleanup behavior', () => {
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
// Start scanning (don't await - let it run in background)
act(() => {
(result.current as any).setIsScanning?.(true);
result.current.scanDevices();
});
// Trigger cleanup
// isScanning should be true now
expect(result.current.isScanning).toBe(true);
// Trigger cleanup while scanning
await act(async () => {
await result.current.cleanupBLE();
});

View File

@ -120,13 +120,17 @@ describe('BLEManager Bulk Operations', () => {
const ssid = 'TestNetwork';
const password = 'testpass123';
// BulkSetWiFi has delays: connect (800ms) + setWiFi (1200ms) + reboot (600ms) = ~2.6s per device
// For 2 devices: ~5.2s, so we need 15s timeout to be safe
jest.setTimeout(15000);
it('should configure WiFi on multiple devices', async () => {
const results = await manager.bulkSetWiFi(devices, ssid, password);
expect(results).toHaveLength(2);
expect(results[0].success).toBe(true);
expect(results[1].success).toBe(true);
});
}, 15000);
it('should return detailed results', async () => {
const results = await manager.bulkSetWiFi(devices.slice(0, 1), ssid, password);
@ -136,7 +140,7 @@ describe('BLEManager Bulk Operations', () => {
deviceName: 'WP_497_81a14c',
success: true,
});
});
}, 10000);
it('should call progress callback for each stage', async () => {
const progressCalls: Array<{
@ -161,7 +165,7 @@ describe('BLEManager Bulk Operations', () => {
expect(progressCalls[1].status).toBe('configuring');
expect(progressCalls[2].status).toBe('rebooting');
expect(progressCalls[3].status).toBe('success');
});
}, 10000);
it('should handle empty device list', async () => {
const results = await manager.bulkSetWiFi([], ssid, password);
@ -178,7 +182,7 @@ describe('BLEManager Bulk Operations', () => {
expect(results[0].success).toBe(false);
expect(results[0].error).toBeDefined();
});
}, 10000);
it('should call error progress callback on failure', async () => {
const progressCalls: string[] = [];
@ -199,7 +203,7 @@ describe('BLEManager Bulk Operations', () => {
);
expect(progressCalls).toContain('error');
});
}, 10000);
it('should continue after one device fails', async () => {
// First device will fail (wrong password), second should still process
@ -216,7 +220,7 @@ describe('BLEManager Bulk Operations', () => {
expect(results).toHaveLength(2);
// Both should have been processed (not short-circuited)
});
}, 15000);
it('should validate WiFi credentials', async () => {
// Invalid SSID with pipe character
@ -228,7 +232,7 @@ describe('BLEManager Bulk Operations', () => {
expect(results[0].success).toBe(false);
expect(results[0].error).toContain('invalid character');
});
}, 10000);
it('should validate password length', async () => {
// Password too short (< 8 chars)
@ -241,7 +245,7 @@ describe('BLEManager Bulk Operations', () => {
expect(results[0].success).toBe(false);
// Mock returns generic invalid credentials error for short password
expect(results[0].error).toContain('invalid');
});
}, 10000);
it('should reboot devices after successful WiFi config', async () => {
await manager.connectDevice('mock-743');
@ -251,7 +255,7 @@ describe('BLEManager Bulk Operations', () => {
// Device should be disconnected after reboot
expect(manager.isDeviceConnected('mock-743')).toBe(false);
});
}, 10000);
});
describe('Bulk Operations - Concurrency', () => {
@ -266,7 +270,7 @@ describe('BLEManager Bulk Operations', () => {
const disconnectResults = await manager.bulkDisconnect(['mock-743', 'mock-769']);
expect(disconnectResults.every(r => r.success)).toBe(true);
});
}, 10000);
it('should track all results independently', async () => {
const devices = [
@ -283,7 +287,7 @@ describe('BLEManager Bulk Operations', () => {
expect(device1Result).toBeDefined();
expect(device2Result).toBeDefined();
expect(device1Result?.deviceId).not.toBe(device2Result?.deviceId);
});
}, 15000);
});
describe('Bulk Operations - Edge Cases', () => {
@ -309,7 +313,7 @@ describe('BLEManager Bulk Operations', () => {
);
expect(results).toHaveLength(2);
});
}, 15000);
it('should handle multiple device lists', async () => {
// Reduced from 10 to 3 devices to avoid timeout

View File

@ -107,7 +107,7 @@ describe('BLE Concurrent Connection Protection', () => {
// The error should mention concurrent connection
const concurrentError = failedEvents.find(e =>
e.error?.includes('Connection already in progress')
e.error?.includes('Already trying to connect') || e.error?.includes('Connection in Progress')
);
expect(concurrentError).toBeDefined();
});