@stoprocent/noble
Version:
A Node.js BLE (Bluetooth Low Energy) central library.
904 lines (736 loc) • 28.5 kB
JavaScript
const Noble = require('../lib/noble');
const Peripheral = require('../lib/peripheral');
const Service = require('../lib/service');
const Characteristic = require('../lib/characteristic');
const Descriptor = require('../lib/descriptor');
describe('noble', () => {
/**
* @type {Noble & import('events').EventEmitter}
*/
let noble;
let mockBindings;
beforeEach(() => {
mockBindings = {
start: jest.fn(),
on: jest.fn(),
startScanning: jest.fn(),
stopScanning: jest.fn(),
connect: jest.fn(),
disconnect: jest.fn(),
updateRssi: jest.fn(),
discoverServices: jest.fn(),
discoverIncludedServices: jest.fn(),
discoverCharacteristics: jest.fn(),
read: jest.fn(),
write: jest.fn(),
broadcast: jest.fn(),
notify: jest.fn(),
discoverDescriptors: jest.fn(),
readValue: jest.fn(),
writeValue: jest.fn(),
readHandle: jest.fn(),
writeHandle: jest.fn(),
reset: jest.fn(),
setScanParameters: jest.fn(),
cancelConnect: jest.fn(),
addressToId: jest.fn()
};
noble = new Noble(mockBindings);
noble.removeAllListeners('warning');
noble._peripherals = new Map();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('startScanning', () => {
test('should delegate to binding', () => {
const expectedServiceUuids = [1, 2, 3];
const expectedAllowDuplicates = true;
noble.startScanning(expectedServiceUuids, expectedAllowDuplicates);
noble.emit('stateChange', 'poweredOn');
// Check for single callback
noble.emit('stateChange', 'poweredOn');
noble.emit('scanStart');
// Check for single callback
noble.emit('scanStart');
expect(mockBindings.startScanning).toHaveBeenCalledWith(
expectedServiceUuids,
expectedAllowDuplicates
);
expect(mockBindings.startScanning).toHaveBeenCalledTimes(1);
});
test('should delegate to callback', async () => {
const expectedServiceUuids = [1, 2, 3];
const expectedAllowDuplicates = true;
const callback = jest.fn();
noble.startScanning(
expectedServiceUuids,
expectedAllowDuplicates,
callback
);
noble.emit('stateChange', 'poweredOn');
// Check for single callback
noble.emit('stateChange', 'poweredOn');
noble.emit('scanStart');
// Check for single callback
noble.emit('scanStart');
expect(callback).toHaveBeenCalledWith(null, undefined);
expect(callback).toHaveBeenCalledTimes(1);
expect(mockBindings.startScanning).toHaveBeenCalledWith(
expectedServiceUuids,
expectedAllowDuplicates
);
expect(mockBindings.startScanning).toHaveBeenCalledTimes(1);
});
test('should delegate to callback, already initialized', async () => {
noble._initialized = true;
noble._state = 'poweredOn';
noble.startScanning();
expect(mockBindings.startScanning).toHaveBeenCalledWith(
undefined,
undefined
);
expect(mockBindings.startScanning).toHaveBeenCalledTimes(1);
});
test('should delegate to callback with filter', async () => {
const expectedServiceUuids = [1, 2, 3];
const expectedAllowDuplicates = true;
const callback = jest.fn();
noble.startScanning(
expectedServiceUuids,
expectedAllowDuplicates,
callback
);
noble.emit('stateChange', 'poweredOn');
// Check for single callback
noble.emit('stateChange', 'poweredOn');
noble.emit('scanStart', 'filter');
expect(callback).toHaveBeenCalledWith(null, 'filter');
expect(callback).toHaveBeenCalledTimes(1);
expect(mockBindings.startScanning).toHaveBeenCalledWith(
expectedServiceUuids,
expectedAllowDuplicates
);
expect(mockBindings.startScanning).toHaveBeenCalledTimes(1);
});
test('should throw an error if not powered on', async () => {
expect(() => {
noble.startScanning();
noble.emit('stateChange', 'poweredOff');
// Check for single callback
noble.emit('stateChange', 'poweredOff');
noble.emit('scanStart');
// Check for single callback
noble.emit('scanStart');
}).toThrow('Could not start scanning, state is poweredOff (not poweredOn)');
expect(mockBindings.startScanning).not.toHaveBeenCalled();
});
});
describe('startScanningAsync', () => {
test('should delegate to binding', async () => {
const expectedServiceUuids = [1, 2, 3];
const expectedAllowDuplicates = true;
const promise = noble.startScanningAsync(
expectedServiceUuids,
expectedAllowDuplicates
);
noble.emit('stateChange', 'poweredOn');
// Check for single callback
noble.emit('stateChange', 'poweredOn');
noble.emit('scanStart');
// Check for single callback
noble.emit('scanStart');
await expect(promise).resolves.toBeUndefined();
expect(mockBindings.startScanning).toHaveBeenCalledWith(
expectedServiceUuids,
expectedAllowDuplicates
);
expect(mockBindings.startScanning).toHaveBeenCalledTimes(1);
});
test('should throw an error if not powered on', async () => {
const promise = noble.startScanningAsync();
noble.emit('stateChange', 'poweredOff');
// Check for single callback
noble.emit('stateChange', 'poweredOff');
noble.emit('scanStart');
// Check for single callback
noble.emit('scanStart');
await expect(promise).rejects.toThrow(
'Could not start scanning, state is poweredOff (not poweredOn)'
);
expect(mockBindings.startScanning).not.toHaveBeenCalled();
});
});
describe('stopScanning', () => {
test('should no callback', async () => {
noble._initialized = true;
noble.stopScanning();
expect(mockBindings.stopScanning).toHaveBeenCalled();
expect(mockBindings.stopScanning).toHaveBeenCalledTimes(1);
});
});
describe('stopScanningAsync', () => {
test('should not delegate to binding (not initialized)', async () => {
const promise = noble.stopScanningAsync();
noble.emit('scanStop');
await expect(promise).rejects.toThrow('Bindings are not initialized');
expect(mockBindings.stopScanning).not.toHaveBeenCalled();
});
test('should delegate to binding (initilazed)', async () => {
noble._initialized = true;
const promise = noble.stopScanningAsync();
noble.emit('scanStop');
await expect(promise).resolves.toBeUndefined();
expect(mockBindings.stopScanning).toHaveBeenCalled();
expect(mockBindings.stopScanning).toHaveBeenCalledTimes(1);
});
});
describe('connect', () => {
test('should delegate to binding', () => {
const peripheralUuid = 'peripheral-uuid';
const parameters = {};
mockBindings.addressToId = jest.fn().mockReturnValue(peripheralUuid);
noble.connect(peripheralUuid, parameters);
expect(mockBindings.connect).toHaveBeenCalledWith(
peripheralUuid,
parameters
);
expect(mockBindings.connect).toHaveBeenCalledTimes(1);
});
});
describe('onConnect', () => {
test('should emit connected on existing peripheral', () => {
const emit = jest.fn();
noble._peripherals.set('uuid', { emit });
const warningCallback = jest.fn();
noble.on('warning', warningCallback);
noble._onConnect('uuid', false);
expect(emit).toHaveBeenCalledWith('connect', false);
expect(emit).toHaveBeenCalledTimes(1);
expect(warningCallback).not.toHaveBeenCalled();
const peripheral = noble._peripherals.get('uuid');
expect(peripheral).toHaveProperty('emit', emit);
expect(peripheral).toHaveProperty('state', 'connected');
});
test('should emit error on existing peripheral', () => {
const emit = jest.fn();
noble._peripherals.set('uuid', { emit });
const warningCallback = jest.fn();
noble.on('warning', warningCallback);
noble._onConnect('uuid', true);
expect(emit).toHaveBeenCalledWith('connect', true);
expect(emit).toHaveBeenCalledTimes(1);
expect(warningCallback).not.toHaveBeenCalled();
const peripheral = noble._peripherals.get('uuid');
expect(peripheral).toHaveProperty('emit', emit);
expect(peripheral).toHaveProperty('state', 'error');
});
test('should emit warning on missing peripheral', () => {
const warningCallback = jest.fn();
noble.on('warning', warningCallback);
noble._onConnect('uuid', true);
expect(warningCallback).toHaveBeenCalledWith('unknown peripheral uuid connected!');
expect(warningCallback).toHaveBeenCalledTimes(1);
expect(noble._peripherals.size).toBe(0);
});
});
describe('setScanParameters', () => {
test('should delegate to binding', async () => {
const interval = 'interval';
const window = 'window';
noble.setScanParameters(interval, window);
noble.emit('scanParametersSet');
expect(mockBindings.setScanParameters).toHaveBeenCalledWith(
interval,
window
);
expect(mockBindings.setScanParameters).toHaveBeenCalledTimes(1);
});
test('should delegate to callback too', async () => {
const interval = 'interval';
const window = 'window';
const callback = jest.fn();
noble.setScanParameters(interval, window, callback);
noble.emit('scanParametersSet');
// Check for single callback
noble.emit('scanParametersSet');
expect(mockBindings.setScanParameters).toHaveBeenCalledWith(
interval,
window
);
expect(mockBindings.setScanParameters).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
});
describe('cancelConnect', () => {
test('should delegate to binding', () => {
const peripheralUuid = 'peripheral-uuid';
const parameters = {};
mockBindings.addressToId = jest.fn().mockReturnValue(peripheralUuid);
noble.cancelConnect(peripheralUuid, parameters);
expect(mockBindings.cancelConnect).toHaveBeenCalledWith(
peripheralUuid,
parameters
);
expect(mockBindings.cancelConnect).toHaveBeenCalledTimes(1);
});
test('should update peripheral state when cancelConnect is called during connection', () => {
// Setup a peripheral in connecting state
const peripheral = {
id: 'test-peripheral',
state: 'connecting',
emit: jest.fn()
};
noble._peripherals.set('test-peripheral', peripheral);
// Mock addressToId to return the peripheral id
mockBindings.addressToId = jest.fn().mockReturnValue('test-peripheral');
// Spy on noble's emit method
const emitSpy = jest.spyOn(noble, 'emit');
// Call cancelConnect
noble.cancelConnect('test-peripheral');
// Verify cancelConnect was called with correct parameters
expect(mockBindings.cancelConnect).toHaveBeenCalledWith(
'test-peripheral',
undefined
);
// Verify peripheral state was updated to disconnected
expect(peripheral.state).toBe('disconnected');
// Verify connect event was emitted with error
expect(emitSpy).toHaveBeenCalledWith(
'connect:test-peripheral',
expect.objectContaining({
message: 'connection canceled!'
})
);
});
});
test('should emit state', () => {
const callback = jest.fn();
noble.on('stateChange', callback);
const state = 'newState';
noble._onStateChange(state);
expect(noble.state).toBe(state);
expect(callback).toHaveBeenCalledWith(state);
expect(callback).toHaveBeenCalledTimes(1);
});
describe('waitForPoweredOnAsync', () => {
test('should resolve when state changes to poweredOn', async () => {
// Set initial state to something other than poweredOn
noble._state = 'poweredOff';
// Create promise but don't await it yet
const promise = noble.waitForPoweredOnAsync();
// Emit state change event to poweredOn
noble.emit('stateChange', 'poweredOn');
// Now await the promise - it should resolve
await expect(promise).resolves.toBeUndefined();
});
test('should resolve immediately if state is already poweredOn', async () => {
// Set initial state to poweredOn
noble._state = 'poweredOn';
// Multiple concurrent calls should resolve immediately
const promise1 = noble.waitForPoweredOnAsync();
const promise2 = noble.waitForPoweredOnAsync();
const promise3 = noble.waitForPoweredOnAsync();
// Both promises should resolve
await expect(promise1).resolves.toBeUndefined();
await expect(promise2).resolves.toBeUndefined();
await expect(promise3).resolves.toBeUndefined();
});
test('should reject if timeout occurs before state changes to poweredOn', async () => {
// Set initial state to something other than poweredOn
noble._state = 'poweredOff';
// Set a very short timeout
const promise = noble.waitForPoweredOnAsync(10);
// Promise should reject after timeout
await expect(promise).rejects.toThrow('Timeout waiting for Noble to be powered on');
});
});
test('should change address', () => {
const address = 'newAddress';
noble._onAddressChange(address);
expect(noble.address).toBe(address);
});
test('should emit scanParametersSet event', () => {
const callback = jest.fn();
noble.on('scanParametersSet', callback);
noble._onScanParametersSet();
expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
test('should emit scanStart event', () => {
const callback = jest.fn();
noble.on('scanStart', callback);
noble._onScanStart('filterDuplicates');
expect(callback).toHaveBeenCalledWith('filterDuplicates');
expect(callback).toHaveBeenCalledTimes(1);
});
test('should emit scanStop event', () => {
const callback = jest.fn();
noble.on('scanStop', callback);
noble._onScanStop();
expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
describe('reset', () => {
test('should reset', () => {
noble.reset();
expect(mockBindings.reset).toHaveBeenCalled();
expect(mockBindings.reset).toHaveBeenCalledTimes(1);
});
});
describe('disconnect', () => {
test('should disconnect', () => {
noble.disconnect('peripheralUuid');
expect(mockBindings.disconnect).toHaveBeenCalledWith('peripheralUuid');
expect(mockBindings.disconnect).toHaveBeenCalledTimes(1);
});
});
describe('onDisconnect', () => {
test('should emit disconnect on existing peripheral', () => {
const emit = jest.fn();
noble._peripherals.set('uuid', { emit });
const warningCallback = jest.fn();
noble.on('warning', warningCallback);
noble._onDisconnect('uuid', false);
expect(emit).toHaveBeenCalledWith('disconnect', false);
expect(emit).toHaveBeenCalledTimes(1);
expect(warningCallback).not.toHaveBeenCalled();
const peripheral = noble._peripherals.get('uuid');
expect(peripheral).toHaveProperty('emit', emit);
expect(peripheral).toHaveProperty('state', 'disconnected');
});
test('should emit warning on missing peripheral', () => {
const warningCallback = jest.fn();
noble.on('warning', warningCallback);
noble._onDisconnect('uuid', true);
expect(warningCallback).toHaveBeenCalledWith('unknown peripheral uuid disconnected!');
expect(warningCallback).toHaveBeenCalledTimes(1);
expect(noble._peripherals.size).toBe(0);
});
});
describe('onDiscover', () => {
test('should add new peripheral', () => {
const uuid = 'uuid';
const address = 'address';
const addressType = 'addressType';
const connectable = 'connectable';
const advertisement = [];
const rssi = 'rssi';
const eventCallback = jest.fn();
noble.on('discover', eventCallback);
noble._onDiscover(
uuid,
address,
addressType,
connectable,
advertisement,
rssi
);
// Check new peripheral
expect(noble._peripherals.has(uuid)).toBe(true);
expect(noble._discoveredPeripherals.has(uuid)).toBe(true);
const peripheral = noble._peripherals.get(uuid);
expect(peripheral._noble).toBe(noble);
expect(peripheral.id).toBe(uuid);
expect(peripheral.address).toBe(address);
expect(peripheral.addressType).toBe(addressType);
expect(peripheral.connectable).toBe(connectable);
expect(peripheral.advertisement).toBe(advertisement);
expect(peripheral.rssi).toBe(rssi);
expect(noble._services[uuid]).toEqual({});
expect(noble._characteristics[uuid]).toEqual({});
expect(noble._descriptors[uuid]).toEqual({});
expect(eventCallback).toHaveBeenCalledWith(peripheral);
expect(eventCallback).toHaveBeenCalledTimes(1);
});
test('should update existing peripheral', () => {
const uuid = 'uuid';
const address = 'address';
const addressType = 'addressType';
const connectable = 'connectable';
const advertisement = [undefined, 'adv2', 'adv3'];
const rssi = 'rssi';
// init peripheral
noble._peripherals.set(uuid, new Peripheral(
noble,
uuid,
'originalAddress',
'originalAddressType',
'originalConnectable',
['adv1'],
'originalRssi'
));
const eventCallback = jest.fn();
noble.on('discover', eventCallback);
noble._onDiscover(
uuid,
address,
addressType,
connectable,
advertisement,
rssi
);
// Check updated peripheral
expect(noble._peripherals.has(uuid)).toBe(true);
expect(noble._discoveredPeripherals.has(uuid)).toBe(true);
const peripheral = noble._peripherals.get(uuid);
expect(peripheral._noble).toBe(noble);
expect(peripheral.id).toBe(uuid);
expect(peripheral.address).toBe('originalAddress');
expect(peripheral.addressType).toBe('originalAddressType');
expect(peripheral.connectable).toBe(connectable);
expect(peripheral.advertisement).toEqual(['adv1', 'adv2', 'adv3']);
expect(peripheral.rssi).toBe(rssi);
expect(Object.keys(noble._services)).toHaveLength(0);
expect(Object.keys(noble._characteristics)).toHaveLength(0);
expect(Object.keys(noble._descriptors)).toHaveLength(0);
expect(eventCallback).toHaveBeenCalledWith(peripheral);
expect(eventCallback).toHaveBeenCalledTimes(1);
});
test('should emit on duplicate', () => {
const uuid = 'uuid';
const address = 'address';
const addressType = 'addressType';
const connectable = 'connectable';
const advertisement = ['adv1', 'adv2', 'adv3'];
const rssi = 'rssi';
// register peripheral
noble._discoveredPeripherals.add(uuid);
noble._allowDuplicates = true;
const eventCallback = jest.fn();
noble.on('discover', eventCallback);
noble._onDiscover(
uuid,
address,
addressType,
connectable,
advertisement,
rssi
);
expect(eventCallback).toHaveBeenCalledWith(noble._peripherals.get(uuid));
expect(eventCallback).toHaveBeenCalledTimes(1);
});
test('should not emit on duplicate', () => {
const uuid = 'uuid';
const address = 'address';
const addressType = 'addressType';
const connectable = 'connectable';
const advertisement = ['adv1', 'adv2', 'adv3'];
const rssi = 'rssi';
// register peripheral
noble._discoveredPeripherals.add(uuid);
noble._allowDuplicates = false;
const eventCallback = jest.fn();
noble.on('discover', eventCallback);
noble._onDiscover(
uuid,
address,
addressType,
connectable,
advertisement,
rssi
);
expect(eventCallback).not.toHaveBeenCalled();
});
test('should emit on new peripheral (even if duplicates are disallowed)', () => {
const uuid = 'uuid';
const address = 'address';
const addressType = 'addressType';
const connectable = 'connectable';
const advertisement = ['adv1', 'adv2', 'adv3'];
const rssi = 'rssi';
const eventCallback = jest.fn();
noble.on('discover', eventCallback);
noble._onDiscover(
uuid,
address,
addressType,
connectable,
advertisement,
rssi
);
expect(eventCallback).toHaveBeenCalledWith(noble._peripherals.get(uuid));
expect(eventCallback).toHaveBeenCalledTimes(1);
});
});
describe('updateRssi', () => {
test('should updateRssi', () => {
noble.updateRssi('peripheralUuid');
expect(mockBindings.updateRssi).toHaveBeenCalledWith('peripheralUuid');
expect(mockBindings.updateRssi).toHaveBeenCalledTimes(1);
});
});
describe('onRssiUpdate', () => {
test('should emit rssiUpdate on existing peripheral', () => {
const emit = jest.fn();
noble._peripherals.set('uuid', { emit });
noble._onRssiUpdate('uuid', 3);
expect(emit).toHaveBeenCalledWith('rssiUpdate', 3, undefined);
expect(emit).toHaveBeenCalledTimes(1);
const peripheral = noble._peripherals.get('uuid');
expect(peripheral).toHaveProperty('emit', emit);
expect(peripheral).toHaveProperty('rssi', 3);
});
test('should emit warning on missing peripheral', () => {
const warningCallback = jest.fn();
noble.on('warning', warningCallback);
noble._onRssiUpdate('uuid', 4);
expect(warningCallback).toHaveBeenCalledWith('unknown peripheral uuid RSSI update!');
expect(warningCallback).toHaveBeenCalledTimes(1);
expect(noble._peripherals.size).toBe(0);
});
});
test('should add multiple services', () => {
noble.addService = jest.fn().mockImplementation((peripheralUuid, service) => service);
const peripheralUuid = 'peripheralUuid';
const services = ['service1', 'service2'];
const result = noble.addServices(peripheralUuid, services);
expect(noble.addService).toHaveBeenCalledTimes(2);
expect(noble.addService).toHaveBeenNthCalledWith(1, peripheralUuid, 'service1');
expect(noble.addService).toHaveBeenNthCalledWith(2, peripheralUuid, 'service2');
expect(result).toEqual(services);
});
describe('addService', () => {
const peripheralUuid = 'peripheralUuid';
const service = {
uuid: 'serviceUuid'
};
const peripheral = {};
beforeEach(() => {
noble._peripherals.set(peripheralUuid, peripheral);
noble._services = { [peripheralUuid]: {} };
noble._characteristics = { [peripheralUuid]: {} };
noble._descriptors = { [peripheralUuid]: {} };
});
test('should add service to lower layer', () => {
noble._bindings.addService = jest.fn();
const result = noble.addService(peripheralUuid, service);
expect(noble._bindings.addService).toHaveBeenCalledWith(peripheralUuid, service);
expect(noble._bindings.addService).toHaveBeenCalledTimes(1);
const expectedService = new Service(noble, peripheralUuid, service.uuid);
expect(result).toEqual(expectedService);
expect(peripheral.services).toEqual([expectedService]);
expect(noble._services).toEqual({
[peripheralUuid]: {
[service.uuid]: expectedService
}
});
expect(noble._characteristics).toEqual({
[peripheralUuid]: {
[service.uuid]: {}
}
});
expect(noble._descriptors).toEqual({
[peripheralUuid]: {
[service.uuid]: {}
}
});
});
test('should add service only to noble', () => {
peripheral.services = [];
const result = noble.addService(peripheralUuid, service);
const expectedService = new Service(noble, peripheralUuid, service.uuid);
expect(result).toEqual(expectedService);
expect(peripheral.services).toEqual([expectedService]);
expect(noble._services).toEqual({
[peripheralUuid]: {
[service.uuid]: expectedService
}
});
expect(noble._characteristics).toEqual({
[peripheralUuid]: {
[service.uuid]: {}
}
});
expect(noble._descriptors).toEqual({
[peripheralUuid]: {
[service.uuid]: {}
}
});
});
});
describe('onServicesDiscovered', () => {
const peripheralUuid = 'peripheralUuid';
const services = ['service1', 'service2'];
test('should not emit servicesDiscovered', () => {
const callback = jest.fn();
noble.on('servicesDiscovered', callback);
noble._onServicesDiscovered(peripheralUuid, services);
expect(callback).not.toHaveBeenCalled();
});
test('should emit servicesDiscovered', () => {
const emit = jest.fn();
noble._peripherals.set(peripheralUuid, { uuid: 'peripheral', emit });
noble._onServicesDiscovered(peripheralUuid, services);
expect(emit).toHaveBeenCalledWith('servicesDiscovered', { uuid: 'peripheral', emit }, services);
expect(emit).toHaveBeenCalledTimes(1);
});
});
test('discoverServices - should delegate to bindings', () => {
noble._bindings.discoverServices = jest.fn();
noble.discoverServices('peripheral', 'uuids');
expect(noble._bindings.discoverServices).toHaveBeenCalledWith('peripheral', 'uuids');
expect(noble._bindings.discoverServices).toHaveBeenCalledTimes(1);
});
describe('onMtu', () => {
test('should update peripheral mtu when set before already', () => {
const peripheral = {
mtu: 234,
emit: jest.fn()
};
noble._peripherals.set('uuid', peripheral);
noble._onMtu('uuid', 123);
expect(peripheral.mtu).toBe(123);
expect(peripheral.emit).toHaveBeenCalledWith('mtu', 123);
expect(peripheral.emit).toHaveBeenCalledTimes(1);
});
test('should update peripheral mtu too when empty', () => {
const peripheral = {
mtu: null,
emit: jest.fn()
};
noble._peripherals.set('uuid', peripheral);
noble._onMtu('uuid', 123);
expect(peripheral.mtu).toBe(123);
expect(peripheral.emit).toHaveBeenCalledWith('mtu', 123);
expect(peripheral.emit).toHaveBeenCalledTimes(1);
});
});
describe('state change handling', () => {
test('should disconnect peripherals when state changes to poweredOff', () => {
// Setup a connected peripheral
const peripheral = {
id: 'test-peripheral',
state: 'connected',
emit: jest.fn()
};
noble._peripherals.set('test-peripheral', peripheral);
noble._state = 'poweredOn';
// Mock the _onDisconnect method to verify it's called
const originalOnDisconnect = noble._onDisconnect;
noble._onDisconnect = jest.fn();
// Trigger state change to poweredOff
noble._onStateChange('poweredOff');
// Check if _onDisconnect was called for the peripheral
expect(noble._onDisconnect).toHaveBeenCalledWith('test-peripheral', 'cleanup');
// Restore original method
noble._onDisconnect = originalOnDisconnect;
});
test('should update peripheral state when cleaned up during poweredOff', () => {
// Setup a connected peripheral
const peripheral = {
id: 'test-peripheral',
state: 'connected',
emit: jest.fn()
};
noble._peripherals.set('test-peripheral', peripheral);
// Call the cleanup method directly
noble._cleanupPeriperals('test-peripheral');
// Verify peripheral emitted disconnect event
expect(peripheral.emit).toHaveBeenCalledWith('disconnect', 'cleanup');
// Verify peripheral state was updated
expect(peripheral.state).toBe('disconnected');
});
});
});