UNPKG

@abandonware/noble

Version:

A Node.js BLE (Bluetooth Low Energy) central library.

919 lines (729 loc) 27.2 kB
const proxyquire = require('proxyquire').noCallThru(); const should = require('should'); const sinon = require('sinon'); const { assert, fake } = sinon; describe('distributed bindings', () => { const wssOn = sinon.stub().resolves(null); const WebSocketServerStub = sinon.stub(); WebSocketServerStub.prototype.on = wssOn; const Bindings = proxyquire('../../../lib/distributed/bindings', { ws: { Server: WebSocketServerStub } }); const baseMessage = { peripheralUuid: 'peripheralUuid', address: 'address', addressType: 'addressType', connectable: 'connectable', advertisement: { localName: 'advertisement.localName', txPowerLevel: 'advertisement.txPowerLevel', serviceUuids: 'advertisement.serviceUuids', manufacturerData: '000102', serviceData: '030405' }, rssi: 'rssi', serviceUuids: 'serviceUuids', serviceUuid: 'serviceUuid', includedServiceUuids: 'includedServiceUuids', characteristics: 'characteristics', characteristicUuid: 'characteristicUuid', isNotification: 'isNotification', state: 'state', descriptors: 'descriptors', descriptorUuid: 'descriptorUuid', handle: 'handle' }; let clock; beforeEach(() => { clock = sinon.useFakeTimers({ toFake: ['nextTick'] }); }); afterEach(() => { clock.restore(); sinon.reset(); }); it('Constructor initialization', () => { const bindings = new Bindings(); assert.calledOnce(WebSocketServerStub); assert.calledWith(WebSocketServerStub, { port: 0xB1e }); assert.calledOnce(wssOn); assert.calledWith(wssOn, 'connection', sinon.match.typeOf('function')); should(bindings.eventNames()).eql(['close', 'message']); }); it('Constructor stateChange event', () => { const stateChange = fake.resolves(null); const bindings = new Bindings(); bindings.on('stateChange', stateChange); clock.tick(1); assert.calledOnce(stateChange); assert.calledWith(stateChange, 'poweredOff'); }); it('_onConnection single client', () => { const stateChange = fake.resolves(null); const fakeWs = { on: fake.resolves(null) }; const bindings = new Bindings(); bindings._wss = { clients: [0] }; bindings.on('stateChange', stateChange); bindings._onConnection(fakeWs); assert.calledOnce(stateChange); assert.calledWith(stateChange, 'poweredOn'); assert.calledTwice(fakeWs.on); assert.calledWith(fakeWs.on, 'close', sinon.match.func); assert.calledWith(fakeWs.on, 'message', sinon.match.func); }); it('_onConnection start scan', () => { const stateChange = fake.resolves(null); const fakeWs = { on: fake.resolves(null), send: fake.resolves(null) }; const bindings = new Bindings(); bindings._startScanCommand = {}; bindings._wss = { clients: [] }; bindings.on('stateChange', stateChange); bindings._onConnection(fakeWs); assert.notCalled(stateChange); assert.calledOnce(fakeWs.send); assert.calledWith(fakeWs.send, '{}'); assert.calledTwice(fakeWs.on); assert.calledWith(fakeWs.on, 'close', sinon.match.func); assert.calledWith(fakeWs.on, 'message', sinon.match.func); }); it('_onClose no clients', () => { const stateChange = fake.resolves(null); const bindings = new Bindings(); bindings._wss = { clients: [] }; bindings.on('stateChange', stateChange); bindings._onClose(null); assert.calledOnce(stateChange); assert.calledWith(stateChange, 'poweredOff'); }); it('_onClose with clients', () => { const stateChange = fake.resolves(null); const bindings = new Bindings(); bindings._wss = { clients: [0] }; bindings.on('stateChange', stateChange); bindings._onClose(null); assert.notCalled(stateChange); }); it('_onMessage -> discover', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('discover', eventFunc); const message = Object.assign({}, baseMessage, { type: 'discover' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); const expectedAdvertisement = { localName: message.advertisement.localName, txPowerLevel: message.advertisement.txPowerLevel, serviceUuids: message.advertisement.serviceUuids, manufacturerData: Buffer.from([0, 1, 2]), serviceData: Buffer.from([3, 4, 5]) }; assert.calledWith(eventFunc, message.peripheralUuid, message.address, message.addressType, message.connectable, expectedAdvertisement, message.rssi); should(bindings._peripherals).keys([message.peripheralUuid]); }); it('_onMessage -> connect', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('connect', eventFunc); const message = Object.assign({}, baseMessage, { type: 'connect' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid); }); it('_onMessage -> disconnect', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('disconnect', eventFunc); const message = Object.assign({}, baseMessage, { type: 'disconnect' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid); }); it('_onMessage -> rssiUpdate', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('rssiUpdate', eventFunc); const message = Object.assign({}, baseMessage, { type: 'rssiUpdate' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.rssi); }); it('_onMessage -> servicesDiscover', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('servicesDiscover', eventFunc); const message = Object.assign({}, baseMessage, { type: 'servicesDiscover' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuids); }); it('_onMessage -> includedServicesDiscover', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('includedServicesDiscover', eventFunc); const message = Object.assign({}, baseMessage, { type: 'includedServicesDiscover' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.includedServiceUuids); }); it('_onMessage -> characteristicsDiscover', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('characteristicsDiscover', eventFunc); const message = Object.assign({}, baseMessage, { type: 'characteristicsDiscover' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristics); }); it('_onMessage -> read', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('read', eventFunc); const message = Object.assign({}, baseMessage, { type: 'read', data: '070809' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristicUuid, Buffer.from([7, 8, 9]), message.isNotification); }); it('_onMessage -> write', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('write', eventFunc); const message = Object.assign({}, baseMessage, { type: 'write' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristicUuid); }); it('_onMessage -> broadcast', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('broadcast', eventFunc); const message = Object.assign({}, baseMessage, { type: 'broadcast' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristicUuid, message.state); }); it('_onMessage -> notify', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('notify', eventFunc); const message = Object.assign({}, baseMessage, { type: 'notify' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristicUuid, message.state); }); it('_onMessage -> descriptorsDiscover', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('descriptorsDiscover', eventFunc); const message = Object.assign({}, baseMessage, { type: 'descriptorsDiscover' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristicUuid, message.descriptors); }); it('_onMessage -> valueRead', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('valueRead', eventFunc); const message = Object.assign({}, baseMessage, { type: 'valueRead', data: '070809' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristicUuid, message.descriptorUuid, Buffer.from([7, 8, 9])); }); it('_onMessage -> valueWrite', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('valueWrite', eventFunc); const message = Object.assign({}, baseMessage, { type: 'valueWrite' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.serviceUuid, message.characteristicUuid, message.descriptorUuid); }); it('_onMessage -> handleRead', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('handleRead', eventFunc); const message = Object.assign({}, baseMessage, { type: 'handleRead', data: '070809' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.handle, Buffer.from([7, 8, 9])); }); it('_onMessage -> handleWrite', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('handleWrite', eventFunc); const message = Object.assign({}, baseMessage, { type: 'handleWrite' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.handle); }); it('_onMessage -> handleNotify', () => { const eventFunc = fake.resolves(null); const bindings = new Bindings(); bindings.on('handleNotify', eventFunc); const message = Object.assign({}, baseMessage, { type: 'handleNotify', data: '070809' }); bindings._onMessage('ws', message); assert.calledOnce(eventFunc); assert.calledWith(eventFunc, message.peripheralUuid, message.handle, Buffer.from([7, 8, 9])); }); it('startScanning', () => { const fakeSend = fake.returns(null); const scanStart = fake.returns(null); const bindings = new Bindings(); bindings._wss = { clients: [{ send: fakeSend }] }; bindings.on('scanStart', scanStart); bindings.startScanning('service-uuids', 'allowDuplicates'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'startScanning', serviceUuids: 'service-uuids', allowDuplicates: 'allowDuplicates' })); assert.calledOnce(scanStart); }); it('stopScanning', () => { const fakeSend = fake.returns(null); const scanStop = fake.returns(null); const bindings = new Bindings(); bindings._wss = { clients: [{ send: fakeSend }] }; bindings.on('scanStop', scanStop); bindings.stopScanning('device-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'stopScanning' })); assert.calledOnce(scanStop); }); it('connect missing peripheral', () => { try { const bindings = new Bindings(); bindings.connect('device-uuid'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('connect on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.connect('device-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'connect', peripheralUuid: 'peripheral-uuid' })); }); it('disconnect missing peripheral', () => { try { const bindings = new Bindings(); bindings.disconnect('device-uuid'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('disconnect on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.disconnect('device-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'disconnect', peripheralUuid: 'peripheral-uuid' })); }); it('updateRssi missing peripheral', () => { try { const bindings = new Bindings(); bindings.updateRssi('device-uuid'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('updateRssi on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.updateRssi('device-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'updateRssi', peripheralUuid: 'peripheral-uuid' })); }); it('discoverServices missing peripheral', () => { try { const bindings = new Bindings(); bindings.discoverServices('device-uuid', 'service-uuids'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('discoverServices on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.discoverServices('device-uuid', 'service-uuids'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'discoverServices', peripheralUuid: 'peripheral-uuid', uuids: 'service-uuids' })); }); it('discoverIncludedServices missing peripheral', () => { try { const bindings = new Bindings(); bindings.discoverIncludedServices('device-uuid', 'service-uuid', 'service-uuids'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('discoverIncludedServices on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.discoverIncludedServices('device-uuid', 'service-uuid', 'service-uuids'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'discoverIncludedServices', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', serviceUuids: 'service-uuids' })); }); it('discoverCharacteristics missing peripheral', () => { try { const bindings = new Bindings(); bindings.discoverCharacteristics('device-uuid', 'service-uuid', 'char-uuid'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('discoverCharacteristics on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.discoverCharacteristics('device-uuid', 'service-uuid', 'char-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'discoverCharacteristics', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuids: 'char-uuid' })); }); it('read missing peripheral', () => { try { const bindings = new Bindings(); bindings.read('device-uuid', 'service-uuid', 'char-uuid'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('read on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.read('device-uuid', 'service-uuid', 'char-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'read', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuid: 'char-uuid' })); }); it('write missing peripheral', () => { try { const bindings = new Bindings(); bindings.write('device-uuid', 'service-uuid', 'char-uuid', 'data', 'withoutReponse'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('write on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.write('device-uuid', 'service-uuid', 'char-uuid', '01', 'withoutResponse'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'write', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuid: 'char-uuid', data: '01', withoutResponse: 'withoutResponse' })); }); it('broadcast missing peripheral', () => { try { const bindings = new Bindings(); bindings.broadcast('device-uuid', 'service-uuid', 'char-uuid', 'broadcast'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('broadcast on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.broadcast('device-uuid', 'service-uuid', 'char-uuid', '01'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'broadcast', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuid: 'char-uuid', broadcast: '01' })); }); it('notify missing peripheral', () => { try { const bindings = new Bindings(); bindings.notify('device-uuid', 'service-uuid', 'char-uuid', 'notify'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('notify on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.notify('device-uuid', 'service-uuid', 'char-uuid', '01'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'notify', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuid: 'char-uuid', notify: '01' })); }); it('discoverDescriptors missing peripheral', () => { try { const bindings = new Bindings(); bindings.discoverDescriptors('device-uuid', 'service-uuid', 'char-uuid'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('discoverDescriptors on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.discoverDescriptors('device-uuid', 'service-uuid', 'char-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'discoverDescriptors', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuid: 'char-uuid' })); }); it('readValue missing peripheral', () => { try { const bindings = new Bindings(); bindings.readValue('device-uuid', 'service-uuid', 'char-uuid', 'descr-uuid'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('readValue on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.readValue('device-uuid', 'service-uuid', 'char-uuid', 'descr-uuid'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'readValue', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuid: 'char-uuid', descriptorUuid: 'descr-uuid' })); }); it('writeValue missing peripheral', () => { try { const bindings = new Bindings(); bindings.writeValue('device-uuid', 'service-uuid', 'char-uuid', 'descr-uuid', '01'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('writeValue on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.writeValue('device-uuid', 'service-uuid', 'char-uuid', 'descr-uuid', '01'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'writeValue', peripheralUuid: 'peripheral-uuid', serviceUuid: 'service-uuid', characteristicUuid: 'char-uuid', descriptorUuid: 'descr-uuid', data: '01' })); }); it('readHandle missing peripheral', () => { try { const bindings = new Bindings(); bindings.readHandle('device-uuid', 'handle'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('readHandle on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.readHandle('device-uuid', 'handle'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'readHandle', peripheralUuid: 'peripheral-uuid', handle: 'handle' })); }); it('writeHandle missing peripheral', () => { try { const bindings = new Bindings(); bindings.writeHandle('device-uuid', 'handle', 'data', 'withoutResponse'); assert.fail('Should throw an error'); } catch (e) { should(e).instanceOf(Error); should(e.message.startsWith('Cannot read') && e.message.includes('\'ws\'') && e.message.includes('undefined')).eql(true); } }); it('writeHandle on existing peripheral', () => { const fakeSend = fake.returns(null); const bindings = new Bindings(); bindings._peripherals['device-uuid'] = { ws: { send: fakeSend }, uuid: 'peripheral-uuid' }; bindings.writeHandle('device-uuid', 'handle', 'data', 'withoutResponse'); assert.calledOnce(fakeSend); assert.calledWith(fakeSend, JSON.stringify({ action: 'readHandle', peripheralUuid: 'peripheral-uuid', handle: 'handle', data: 'data', withoutResponse: 'withoutResponse' })); }); });