@stoprocent/noble 1.19.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +393 -650
- package/examples/advertisement-discovery.js +57 -48
- package/examples/connect-address.js +59 -34
- package/examples/echo.js +59 -69
- package/examples/enter-exit.js +55 -49
- package/examples/multiple-bindings.js +53 -0
- package/examples/peripheral-explorer-async.js +39 -21
- package/examples/peripheral-explorer.ts +52 -0
- package/index.d.ts +249 -209
- package/index.js +4 -1
- package/jest.config.js +4 -0
- package/lib/characteristic.js +153 -127
- package/lib/{win/src/callbacks.h → common/include/Emit.h} +17 -14
- package/lib/common/include/Peripheral.h +31 -0
- package/lib/common/include/ThreadSafeCallback.h +95 -0
- package/lib/{win/src/callbacks.cc → common/src/Emit.cc} +111 -68
- package/lib/descriptor.js +57 -54
- package/lib/hci-socket/acl-stream.js +2 -4
- package/lib/hci-socket/bindings.js +96 -73
- package/lib/hci-socket/gap.js +2 -3
- package/lib/hci-socket/gatt.js +2 -5
- package/lib/hci-socket/hci.js +19 -7
- package/lib/hci-socket/signaling.js +2 -3
- package/lib/hci-socket/smp.js +2 -3
- package/lib/hci-socket/vs.js +1 -0
- package/lib/mac/binding.gyp +5 -7
- package/lib/mac/bindings.js +1 -3
- package/lib/mac/src/ble_manager.h +1 -8
- package/lib/mac/src/ble_manager.mm +87 -44
- package/lib/mac/src/napi_objc.h +1 -0
- package/lib/mac/src/napi_objc.mm +0 -6
- package/lib/mac/src/noble_mac.h +5 -3
- package/lib/mac/src/noble_mac.mm +99 -57
- package/lib/mac/src/objc_cpp.h +3 -2
- package/lib/mac/src/objc_cpp.mm +0 -6
- package/lib/noble.js +579 -488
- package/lib/peripheral.js +171 -174
- package/lib/resolve-bindings.js +37 -30
- package/lib/service.js +58 -55
- package/lib/win/binding.gyp +4 -11
- package/lib/win/bindings.js +1 -3
- package/lib/win/src/ble_manager.cc +291 -166
- package/lib/win/src/ble_manager.h +11 -13
- package/lib/win/src/napi_winrt.cc +1 -7
- package/lib/win/src/napi_winrt.h +1 -1
- package/lib/win/src/noble_winrt.cc +88 -61
- package/lib/win/src/noble_winrt.h +5 -3
- package/lib/win/src/notify_map.cc +0 -7
- package/lib/win/src/notify_map.h +1 -8
- package/lib/win/src/peripheral_winrt.cc +29 -11
- package/lib/win/src/peripheral_winrt.h +1 -1
- package/lib/win/src/radio_watcher.cc +79 -69
- package/lib/win/src/radio_watcher.h +30 -11
- package/lib/win/src/winrt_cpp.cc +1 -1
- package/lib/win/src/winrt_cpp.h +3 -0
- package/package.json +14 -17
- package/prebuilds/darwin-x64+arm64/@stoprocent+noble.node +0 -0
- package/prebuilds/win32-ia32/@stoprocent+noble.node +0 -0
- package/prebuilds/win32-x64/@stoprocent+noble.node +0 -0
- package/test/lib/characteristic.test.js +202 -322
- package/test/lib/descriptor.test.js +62 -95
- package/test/lib/hci-socket/acl-stream.test.js +112 -108
- package/test/lib/hci-socket/bindings.test.js +576 -365
- package/test/lib/hci-socket/hci.test.js +442 -473
- package/test/lib/hci-socket/signaling.test.js +45 -48
- package/test/lib/hci-socket/smp.test.js +144 -142
- package/test/lib/hci-socket/vs.test.js +193 -18
- package/test/lib/peripheral.test.js +492 -322
- package/test/lib/resolve-bindings.test.js +207 -82
- package/test/lib/service.test.js +79 -88
- package/test/noble.test.js +381 -1085
- package/.editorconfig +0 -11
- package/.nycrc.json +0 -4
- package/codecov.yml +0 -5
- package/examples/cache-gatt-discovery.js +0 -198
- package/examples/cache-gatt-reconnect.js +0 -164
- package/examples/ext-advertisement-discovery.js +0 -65
- package/examples/peripheral-explorer.js +0 -225
- package/examples/pizza/central.js +0 -194
- package/examples/pizza/pizza.js +0 -60
- package/examples/test/test.custom.js +0 -131
- package/examples/uart-bind-params.js +0 -28
- package/lib/distributed/bindings.js +0 -326
- package/lib/mac/src/callbacks.cc +0 -222
- package/lib/mac/src/callbacks.h +0 -84
- package/lib/mac/src/peripheral.h +0 -23
- package/lib/resolve-bindings-web.js +0 -9
- package/lib/webbluetooth/bindings.js +0 -368
- package/lib/websocket/bindings.js +0 -321
- package/lib/win/src/peripheral.h +0 -23
- package/test/lib/distributed/bindings.test.js +0 -918
- package/test/lib/webbluetooth/bindings.test.js +0 -190
- package/test/lib/websocket/bindings.test.js +0 -456
- package/test/mocha.setup.js +0 -0
- package/with-bindings.js +0 -5
- package/with-custom-binding.js +0 -6
- package/ws-slave.js +0 -404
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
const should = require('should');
|
|
2
|
-
const sinon = require('sinon');
|
|
3
|
-
const { fake, assert } = sinon;
|
|
4
|
-
|
|
5
1
|
const Peripheral = require('../../lib/peripheral');
|
|
6
2
|
|
|
7
3
|
describe('peripheral', () => {
|
|
@@ -18,287 +14,272 @@ describe('peripheral', () => {
|
|
|
18
14
|
let peripheral = null;
|
|
19
15
|
|
|
20
16
|
beforeEach(() => {
|
|
21
|
-
mockNoble = {
|
|
22
|
-
|
|
17
|
+
mockNoble = {
|
|
18
|
+
_withDisconnectHandler: (id, operation) => {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
return Promise.resolve(operation())
|
|
21
|
+
.then(result => {
|
|
22
|
+
resolve(result);
|
|
23
|
+
})
|
|
24
|
+
.catch(error => {
|
|
25
|
+
reject(error);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
connect: jest.fn(),
|
|
30
|
+
cancelConnect: jest.fn(),
|
|
31
|
+
disconnect: jest.fn(),
|
|
32
|
+
updateRssi: jest.fn(),
|
|
33
|
+
discoverServices: jest.fn(),
|
|
34
|
+
readHandle: jest.fn(),
|
|
35
|
+
writeHandle: jest.fn()
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
peripheral = new Peripheral(
|
|
39
|
+
mockNoble,
|
|
40
|
+
mockId,
|
|
41
|
+
mockAddress,
|
|
42
|
+
mockAddressType,
|
|
43
|
+
mockConnectable,
|
|
44
|
+
mockAdvertisement,
|
|
45
|
+
mockRssi
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
jest.clearAllMocks();
|
|
23
51
|
});
|
|
24
52
|
|
|
25
|
-
|
|
26
|
-
|
|
53
|
+
test('should have a id', () => {
|
|
54
|
+
expect(peripheral.id).toEqual(mockId);
|
|
27
55
|
});
|
|
28
56
|
|
|
29
|
-
|
|
30
|
-
|
|
57
|
+
test('should have an address', () => {
|
|
58
|
+
expect(peripheral.address).toEqual(mockAddress);
|
|
31
59
|
});
|
|
32
60
|
|
|
33
|
-
|
|
34
|
-
|
|
61
|
+
test('should have an address type', () => {
|
|
62
|
+
expect(peripheral.addressType).toEqual(mockAddressType);
|
|
35
63
|
});
|
|
36
64
|
|
|
37
|
-
|
|
38
|
-
|
|
65
|
+
test('should have connectable', () => {
|
|
66
|
+
expect(peripheral.connectable).toEqual(mockConnectable);
|
|
39
67
|
});
|
|
40
68
|
|
|
41
|
-
|
|
42
|
-
|
|
69
|
+
test('should have advertisement', () => {
|
|
70
|
+
expect(peripheral.advertisement).toEqual(mockAdvertisement);
|
|
43
71
|
});
|
|
44
72
|
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
test('should have rssi', () => {
|
|
74
|
+
expect(peripheral.rssi).toEqual(mockRssi);
|
|
47
75
|
});
|
|
48
76
|
|
|
49
77
|
describe('toString', () => {
|
|
50
|
-
|
|
51
|
-
|
|
78
|
+
test('should be id, address, address type, connectable, advertisement, rssi, state', () => {
|
|
79
|
+
expect(peripheral.toString()).toEqual(
|
|
80
|
+
'{"id":"mock-id","address":"mock-address","addressType":"mock-address-type","connectable":"mock-connectable","advertisement":"mock-advertisement","rssi":"mock-rssi","mtu":null,"state":"disconnected"}'
|
|
81
|
+
);
|
|
52
82
|
});
|
|
53
83
|
});
|
|
54
84
|
|
|
55
85
|
describe('connect', () => {
|
|
56
|
-
|
|
57
|
-
mockNoble.connect = sinon.spy();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
afterEach(() => {
|
|
61
|
-
sinon.reset();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should delegate to noble', () => {
|
|
86
|
+
test('should delegate to noble', () => {
|
|
65
87
|
peripheral.connect();
|
|
66
88
|
|
|
67
|
-
|
|
89
|
+
expect(mockNoble.connect).toHaveBeenCalledWith(mockId, undefined);
|
|
90
|
+
expect(mockNoble.connect).toHaveBeenCalledTimes(1);
|
|
68
91
|
});
|
|
69
92
|
|
|
70
|
-
|
|
71
|
-
const callback =
|
|
93
|
+
test('should callback', () => {
|
|
94
|
+
const callback = jest.fn();
|
|
72
95
|
|
|
73
96
|
peripheral.connect(callback);
|
|
74
97
|
peripheral.emit('connect', 'error');
|
|
75
98
|
|
|
76
|
-
|
|
77
|
-
|
|
99
|
+
expect(callback).toHaveBeenCalledWith('error');
|
|
100
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
101
|
+
expect(mockNoble.connect).toHaveBeenCalledWith(mockId, undefined);
|
|
102
|
+
expect(mockNoble.connect).toHaveBeenCalledTimes(1);
|
|
78
103
|
});
|
|
79
104
|
|
|
80
|
-
|
|
105
|
+
test('with options, no callback', () => {
|
|
81
106
|
const options = { options: true };
|
|
82
107
|
|
|
83
108
|
peripheral.connect(options);
|
|
84
109
|
peripheral.emit('connect');
|
|
85
110
|
|
|
86
|
-
|
|
111
|
+
expect(mockNoble.connect).toHaveBeenCalledWith(mockId, options);
|
|
112
|
+
expect(mockNoble.connect).toHaveBeenCalledTimes(1);
|
|
87
113
|
});
|
|
88
114
|
|
|
89
|
-
|
|
115
|
+
test('both options and callback', () => {
|
|
90
116
|
const options = { options: true };
|
|
91
|
-
const callback =
|
|
117
|
+
const callback = jest.fn();
|
|
92
118
|
|
|
93
119
|
peripheral.connect(options, callback);
|
|
94
120
|
peripheral.emit('connect');
|
|
95
121
|
|
|
96
|
-
|
|
97
|
-
|
|
122
|
+
expect(callback).toHaveBeenCalledWith(undefined);
|
|
123
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
124
|
+
expect(mockNoble.connect).toHaveBeenCalledWith(mockId, options);
|
|
125
|
+
expect(mockNoble.connect).toHaveBeenCalledTimes(1);
|
|
98
126
|
});
|
|
99
127
|
});
|
|
100
128
|
|
|
101
129
|
describe('connectAsync', () => {
|
|
102
|
-
|
|
103
|
-
mockNoble.connect = sinon.spy();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
afterEach(() => {
|
|
107
|
-
sinon.reset();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should resolve', async () => {
|
|
130
|
+
test('should resolve', async () => {
|
|
111
131
|
const promise = peripheral.connectAsync();
|
|
112
132
|
peripheral.emit('connect');
|
|
113
133
|
|
|
114
|
-
|
|
134
|
+
await expect(promise).resolves.toBeUndefined();
|
|
115
135
|
});
|
|
116
136
|
|
|
117
|
-
|
|
137
|
+
test('should reject on error', async () => {
|
|
118
138
|
const promise = peripheral.connectAsync();
|
|
119
139
|
peripheral.emit('connect', new Error('error'));
|
|
120
|
-
|
|
121
|
-
|
|
140
|
+
|
|
141
|
+
await expect(promise).rejects.toThrow('error');
|
|
122
142
|
});
|
|
123
143
|
|
|
124
|
-
|
|
144
|
+
test('should delegate to noble', async () => {
|
|
125
145
|
const promise = peripheral.connectAsync();
|
|
126
146
|
peripheral.emit('connect');
|
|
127
147
|
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
await expect(promise).resolves.toBeUndefined();
|
|
149
|
+
expect(mockNoble.connect).toHaveBeenCalledWith(mockId, undefined);
|
|
150
|
+
expect(mockNoble.connect).toHaveBeenCalledTimes(1);
|
|
130
151
|
});
|
|
131
152
|
|
|
132
|
-
|
|
153
|
+
test('with options', async () => {
|
|
133
154
|
const options = { options: true };
|
|
134
155
|
|
|
135
156
|
const promise = peripheral.connectAsync(options);
|
|
136
157
|
peripheral.emit('connect');
|
|
137
158
|
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
await expect(promise).resolves.toBeUndefined();
|
|
160
|
+
expect(mockNoble.connect).toHaveBeenCalledWith(mockId, options);
|
|
161
|
+
expect(mockNoble.connect).toHaveBeenCalledTimes(1);
|
|
140
162
|
});
|
|
141
163
|
});
|
|
142
164
|
|
|
143
165
|
describe('cancelConnect', () => {
|
|
144
|
-
|
|
145
|
-
mockNoble.connect = sinon.spy();
|
|
146
|
-
mockNoble.cancelConnect = sinon.spy();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
afterEach(() => {
|
|
150
|
-
sinon.reset();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('not connecting, should resolve', async () => {
|
|
166
|
+
test('not connecting, should resolve', async () => {
|
|
154
167
|
await peripheral.cancelConnect();
|
|
155
168
|
|
|
156
|
-
|
|
169
|
+
expect(mockNoble.cancelConnect).not.toHaveBeenCalled();
|
|
157
170
|
});
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
test('connecting, should emit connect with error', async () => {
|
|
160
173
|
const options = { options: true };
|
|
161
|
-
const connectCallback =
|
|
174
|
+
const connectCallback = jest.fn();
|
|
162
175
|
|
|
163
176
|
peripheral.connect(connectCallback);
|
|
164
177
|
peripheral.cancelConnect(options);
|
|
165
178
|
|
|
166
|
-
|
|
167
|
-
|
|
179
|
+
expect(connectCallback).toHaveBeenCalledWith(expect.objectContaining({
|
|
180
|
+
message: 'connection canceled!'
|
|
181
|
+
}));
|
|
182
|
+
expect(mockNoble.cancelConnect).toHaveBeenCalledWith(mockId, options);
|
|
183
|
+
expect(mockNoble.cancelConnect).toHaveBeenCalledTimes(1);
|
|
168
184
|
});
|
|
169
185
|
});
|
|
170
186
|
|
|
171
187
|
describe('disconnect', () => {
|
|
172
|
-
|
|
173
|
-
mockNoble.disconnect = sinon.spy();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
afterEach(() => {
|
|
177
|
-
sinon.reset();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should delegate to noble', () => {
|
|
188
|
+
test('should delegate to noble', () => {
|
|
181
189
|
peripheral.disconnect();
|
|
182
|
-
|
|
190
|
+
expect(mockNoble.disconnect).toHaveBeenCalledWith(mockId);
|
|
191
|
+
expect(mockNoble.disconnect).toHaveBeenCalledTimes(1);
|
|
183
192
|
});
|
|
184
193
|
|
|
185
|
-
|
|
186
|
-
const callback =
|
|
194
|
+
test('should callback', () => {
|
|
195
|
+
const callback = jest.fn();
|
|
187
196
|
|
|
188
197
|
peripheral.disconnect(callback);
|
|
189
198
|
peripheral.emit('disconnect');
|
|
190
199
|
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
expect(callback).toHaveBeenCalledWith(null);
|
|
201
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
202
|
+
expect(mockNoble.disconnect).toHaveBeenCalledWith(mockId);
|
|
203
|
+
expect(mockNoble.disconnect).toHaveBeenCalledTimes(1);
|
|
193
204
|
});
|
|
194
205
|
});
|
|
195
206
|
|
|
196
207
|
describe('disconnectAsync', () => {
|
|
197
|
-
|
|
198
|
-
mockNoble.disconnect = sinon.spy();
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
afterEach(() => {
|
|
202
|
-
sinon.reset();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('should delegate to noble', () => {
|
|
208
|
+
test('should delegate to noble', async () => {
|
|
206
209
|
const promise = peripheral.disconnectAsync();
|
|
207
210
|
peripheral.emit('disconnect');
|
|
208
211
|
|
|
209
|
-
|
|
210
|
-
|
|
212
|
+
await expect(promise).resolves.toBeUndefined();
|
|
213
|
+
expect(mockNoble.disconnect).toHaveBeenCalledWith(mockId);
|
|
214
|
+
expect(mockNoble.disconnect).toHaveBeenCalledTimes(1);
|
|
211
215
|
});
|
|
212
216
|
});
|
|
213
217
|
|
|
214
218
|
describe('updateRssi', () => {
|
|
215
|
-
|
|
216
|
-
mockNoble.updateRssi = sinon.spy();
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
afterEach(() => {
|
|
220
|
-
sinon.reset();
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('should delegate to noble', () => {
|
|
219
|
+
test('should delegate to noble', () => {
|
|
224
220
|
peripheral.updateRssi();
|
|
225
|
-
|
|
221
|
+
expect(mockNoble.updateRssi).toHaveBeenCalledWith(mockId);
|
|
222
|
+
expect(mockNoble.updateRssi).toHaveBeenCalledTimes(1);
|
|
226
223
|
});
|
|
227
224
|
|
|
228
|
-
|
|
229
|
-
const callback =
|
|
225
|
+
test('should callback', () => {
|
|
226
|
+
const callback = jest.fn();
|
|
230
227
|
|
|
231
228
|
peripheral.updateRssi(callback);
|
|
232
229
|
peripheral.emit('rssiUpdate', 'new-rssi');
|
|
233
230
|
|
|
234
|
-
|
|
235
|
-
|
|
231
|
+
expect(callback).toHaveBeenCalledWith(undefined, 'new-rssi');
|
|
232
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
233
|
+
expect(mockNoble.updateRssi).toHaveBeenCalledWith(mockId);
|
|
234
|
+
expect(mockNoble.updateRssi).toHaveBeenCalledTimes(1);
|
|
236
235
|
});
|
|
237
236
|
});
|
|
238
237
|
|
|
239
238
|
describe('updateRssiAsync', () => {
|
|
240
|
-
|
|
241
|
-
mockNoble.updateRssi = sinon.spy();
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
afterEach(() => {
|
|
245
|
-
sinon.reset();
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('should resolve with rssi', async () => {
|
|
239
|
+
test('should resolve with rssi', async () => {
|
|
249
240
|
const promise = peripheral.updateRssiAsync();
|
|
250
241
|
peripheral.emit('rssiUpdate', 'new-rssi');
|
|
251
|
-
|
|
242
|
+
|
|
243
|
+
await expect(promise).resolves.toEqual('new-rssi');
|
|
252
244
|
});
|
|
253
245
|
});
|
|
254
246
|
|
|
255
247
|
describe('discoverServices', () => {
|
|
256
|
-
|
|
257
|
-
mockNoble.discoverServices = sinon.spy();
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
afterEach(() => {
|
|
261
|
-
sinon.reset();
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('should delegate to noble', () => {
|
|
248
|
+
test('should delegate to noble', () => {
|
|
265
249
|
peripheral.discoverServices();
|
|
266
|
-
|
|
250
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledWith(mockId, undefined);
|
|
251
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledTimes(1);
|
|
267
252
|
});
|
|
268
253
|
|
|
269
|
-
|
|
254
|
+
test('should delegate to noble, service uuids', () => {
|
|
270
255
|
const mockServiceUuids = [];
|
|
271
256
|
peripheral.discoverServices(mockServiceUuids);
|
|
272
|
-
|
|
257
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledWith(mockId, mockServiceUuids);
|
|
258
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledTimes(1);
|
|
273
259
|
});
|
|
274
260
|
|
|
275
|
-
|
|
276
|
-
const callback =
|
|
261
|
+
test('should callback', () => {
|
|
262
|
+
const callback = jest.fn();
|
|
277
263
|
peripheral.discoverServices('uuids', callback);
|
|
278
264
|
peripheral.emit('servicesDiscover', 'services');
|
|
279
265
|
|
|
280
|
-
|
|
281
|
-
|
|
266
|
+
expect(callback).toHaveBeenCalledWith(undefined, 'services');
|
|
267
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
268
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledWith(mockId, 'uuids');
|
|
269
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledTimes(1);
|
|
282
270
|
});
|
|
283
271
|
});
|
|
284
272
|
|
|
285
273
|
describe('discoverServicesAsync', () => {
|
|
286
|
-
|
|
287
|
-
mockNoble.discoverServices = sinon.spy();
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
afterEach(() => {
|
|
291
|
-
sinon.reset();
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it('should resolve with services', async () => {
|
|
274
|
+
test('should resolve with services', async () => {
|
|
295
275
|
const mockServices = 'discoveredServices';
|
|
296
276
|
|
|
297
277
|
const promise = peripheral.discoverServicesAsync('uuids');
|
|
298
278
|
peripheral.emit('servicesDiscover', mockServices);
|
|
299
279
|
|
|
300
|
-
|
|
301
|
-
|
|
280
|
+
await expect(promise).resolves.toEqual(mockServices);
|
|
281
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledWith(mockId, 'uuids');
|
|
282
|
+
expect(mockNoble.discoverServices).toHaveBeenCalledTimes(1);
|
|
302
283
|
});
|
|
303
284
|
});
|
|
304
285
|
|
|
@@ -308,74 +289,78 @@ describe('peripheral', () => {
|
|
|
308
289
|
let mockServices = null;
|
|
309
290
|
|
|
310
291
|
beforeEach(() => {
|
|
311
|
-
peripheral.discoverServices =
|
|
292
|
+
peripheral.discoverServices = jest.fn((uuids, callback) => {
|
|
293
|
+
if (callback) callback(null, mockServices);
|
|
294
|
+
});
|
|
312
295
|
|
|
313
296
|
mockServices = [
|
|
314
297
|
{
|
|
315
298
|
uuid: '1',
|
|
316
|
-
discoverCharacteristics:
|
|
299
|
+
discoverCharacteristics: jest.fn((charUuids, callback) => {
|
|
300
|
+
if (callback) callback(null, []);
|
|
301
|
+
})
|
|
317
302
|
},
|
|
318
303
|
{
|
|
319
304
|
uuid: '2',
|
|
320
|
-
discoverCharacteristics:
|
|
305
|
+
discoverCharacteristics: jest.fn((charUuids, callback) => {
|
|
306
|
+
if (callback) callback(null, []);
|
|
307
|
+
})
|
|
321
308
|
}
|
|
322
309
|
];
|
|
323
310
|
});
|
|
324
311
|
|
|
325
|
-
|
|
326
|
-
sinon.reset();
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
it('should call discoverServices', () => {
|
|
312
|
+
test('should call discoverServices', () => {
|
|
330
313
|
peripheral.discoverSomeServicesAndCharacteristics(mockServiceUuids);
|
|
331
|
-
|
|
314
|
+
|
|
332
315
|
|
|
333
|
-
|
|
316
|
+
expect(peripheral.discoverServices).toHaveBeenCalled();
|
|
317
|
+
// expect(peripheral.discoverServices.mock.calls[0][0]).toEqual(mockServiceUuids);
|
|
318
|
+
expect(typeof peripheral.discoverServices.mock.calls[0][1]).toBe('function');
|
|
334
319
|
});
|
|
335
320
|
|
|
336
|
-
|
|
321
|
+
test('should call discoverCharacteristics on each service discovered', () => {
|
|
337
322
|
peripheral.discoverSomeServicesAndCharacteristics(mockServiceUuids, mockCharacteristicUuids);
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
323
|
+
|
|
324
|
+
expect(peripheral.discoverServices).toHaveBeenCalled();
|
|
325
|
+
expect(mockServices[0].discoverCharacteristics).toHaveBeenCalled();
|
|
326
|
+
expect(mockServices[1].discoverCharacteristics).toHaveBeenCalled();
|
|
327
|
+
|
|
328
|
+
// expect(mockServices[0].discoverCharacteristics.mock.calls[0][0]).toEqual(mockCharacteristicUuids);
|
|
329
|
+
expect(typeof mockServices[0].discoverCharacteristics.mock.calls[0][1]).toBe('function');
|
|
343
330
|
});
|
|
344
331
|
|
|
345
|
-
|
|
346
|
-
const callback =
|
|
332
|
+
test('should callback', () => {
|
|
333
|
+
const callback = jest.fn();
|
|
347
334
|
|
|
348
335
|
peripheral.discoverSomeServicesAndCharacteristics(mockServiceUuids, mockCharacteristicUuids, callback);
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
mockServices[0].discoverCharacteristics.callArg(1, null, mockCharacteristicUuids);
|
|
356
|
-
mockServices[1].discoverCharacteristics.callArg(1, null, mockCharacteristicUuids);
|
|
357
|
-
|
|
358
|
-
assert.calledOnceWithExactly(callback, null, mockServices, mockCharacteristicUuids);
|
|
336
|
+
|
|
337
|
+
expect(peripheral.discoverServices).toHaveBeenCalled();
|
|
338
|
+
expect(mockServices[0].discoverCharacteristics).toHaveBeenCalled();
|
|
339
|
+
expect(mockServices[1].discoverCharacteristics).toHaveBeenCalled();
|
|
340
|
+
expect(callback).toHaveBeenCalledWith(null, mockServices, []);
|
|
359
341
|
});
|
|
360
342
|
|
|
361
|
-
|
|
362
|
-
const callback =
|
|
363
|
-
|
|
364
|
-
peripheral.discoverSomeServicesAndCharacteristics(mockServiceUuids, mockCharacteristicUuids, callback);
|
|
365
|
-
peripheral.discoverServices.callArg(1, null, mockServices);
|
|
366
|
-
|
|
343
|
+
test('should callback with the services and characteristics discovered', () => {
|
|
344
|
+
const callback = jest.fn();
|
|
367
345
|
const mockCharacteristic1 = { uuid: '1' };
|
|
368
346
|
const mockCharacteristic2 = { uuid: '2' };
|
|
369
347
|
const mockCharacteristic3 = { uuid: '3' };
|
|
370
348
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
mockServices[
|
|
376
|
-
|
|
349
|
+
mockServices[0].discoverCharacteristics = jest.fn((charUuids, callback) => {
|
|
350
|
+
if (callback) callback(null, [mockCharacteristic1]);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
mockServices[1].discoverCharacteristics = jest.fn((charUuids, callback) => {
|
|
354
|
+
if (callback) callback(null, [mockCharacteristic2, mockCharacteristic3]);
|
|
355
|
+
});
|
|
377
356
|
|
|
378
|
-
|
|
357
|
+
peripheral.discoverSomeServicesAndCharacteristics(mockServiceUuids, mockCharacteristicUuids, callback);
|
|
358
|
+
|
|
359
|
+
expect(callback).toHaveBeenCalledWith(
|
|
360
|
+
null,
|
|
361
|
+
mockServices,
|
|
362
|
+
[mockCharacteristic1, mockCharacteristic2, mockCharacteristic3]
|
|
363
|
+
);
|
|
379
364
|
});
|
|
380
365
|
});
|
|
381
366
|
|
|
@@ -385,239 +370,424 @@ describe('peripheral', () => {
|
|
|
385
370
|
let mockServices = null;
|
|
386
371
|
|
|
387
372
|
beforeEach(() => {
|
|
388
|
-
peripheral.discoverServices =
|
|
373
|
+
peripheral.discoverServices = jest.fn();
|
|
389
374
|
|
|
390
375
|
mockServices = [
|
|
391
376
|
{
|
|
392
377
|
uuid: '1',
|
|
393
|
-
discoverCharacteristics:
|
|
378
|
+
discoverCharacteristics: jest.fn()
|
|
394
379
|
},
|
|
395
380
|
{
|
|
396
381
|
uuid: '2',
|
|
397
|
-
discoverCharacteristics:
|
|
382
|
+
discoverCharacteristics: jest.fn()
|
|
398
383
|
}
|
|
399
384
|
];
|
|
400
385
|
});
|
|
401
386
|
|
|
402
|
-
|
|
403
|
-
sinon.reset();
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
it('should call discoverServices', async () => {
|
|
387
|
+
test('should call discoverServices', async () => {
|
|
407
388
|
const promise = peripheral.discoverSomeServicesAndCharacteristicsAsync(mockServiceUuids);
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
389
|
+
|
|
390
|
+
// Call the callback passed to discoverServices
|
|
391
|
+
peripheral.discoverServices.mock.calls[0][1](null, mockServices);
|
|
392
|
+
|
|
393
|
+
// Call the callbacks for each service's discoverCharacteristics
|
|
394
|
+
mockServices[0].discoverCharacteristics.mock.calls[0][1](null, []);
|
|
395
|
+
mockServices[1].discoverCharacteristics.mock.calls[0][1](null, []);
|
|
396
|
+
|
|
397
|
+
await expect(promise).resolves.toEqual({ characteristics: [], services: mockServices });
|
|
398
|
+
expect(peripheral.discoverServices).toHaveBeenCalled();
|
|
399
|
+
expect(peripheral.discoverServices.mock.calls[0][0]).toEqual(mockServiceUuids);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test('should call discoverCharacteristics on each service discovered', async () => {
|
|
415
403
|
const promise = peripheral.discoverSomeServicesAndCharacteristicsAsync(mockServiceUuids, mockCharacteristicUuids);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
404
|
+
|
|
405
|
+
// Call the callback passed to discoverServices
|
|
406
|
+
peripheral.discoverServices.mock.calls[0][1](null, mockServices);
|
|
407
|
+
|
|
408
|
+
// Call the callbacks for each service's discoverCharacteristics
|
|
409
|
+
mockServices[0].discoverCharacteristics.mock.calls[0][1](null, []);
|
|
410
|
+
mockServices[1].discoverCharacteristics.mock.calls[0][1](null, []);
|
|
411
|
+
|
|
412
|
+
await expect(promise).resolves.toEqual({ characteristics: [], services: mockServices });
|
|
413
|
+
expect(peripheral.discoverServices).toHaveBeenCalled();
|
|
414
|
+
expect(mockServices[0].discoverCharacteristics).toHaveBeenCalled();
|
|
415
|
+
expect(mockServices[1].discoverCharacteristics).toHaveBeenCalled();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test('should reject on error', async () => {
|
|
425
419
|
const promise = peripheral.discoverSomeServicesAndCharacteristicsAsync(mockServiceUuids, mockCharacteristicUuids);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
420
|
+
|
|
421
|
+
// Call the callback passed to discoverServices with an error
|
|
422
|
+
peripheral.discoverServices.mock.calls[0][1]('error', null);
|
|
423
|
+
|
|
424
|
+
await expect(promise).rejects.toEqual('error');
|
|
425
|
+
expect(peripheral.discoverServices).toHaveBeenCalled();
|
|
426
|
+
expect(mockServices[0].discoverCharacteristics).not.toHaveBeenCalled();
|
|
431
427
|
});
|
|
432
428
|
|
|
433
|
-
|
|
434
|
-
const callback = sinon.spy();
|
|
435
|
-
|
|
436
|
-
const promise = peripheral.discoverSomeServicesAndCharacteristicsAsync(mockServiceUuids, mockCharacteristicUuids, callback);
|
|
437
|
-
peripheral.discoverServices.callArg(1, null, mockServices);
|
|
438
|
-
|
|
429
|
+
test('should resolve with the services and characteristics discovered', async () => {
|
|
439
430
|
const mockCharacteristic1 = { uuid: '1' };
|
|
440
431
|
const mockCharacteristic2 = { uuid: '2' };
|
|
441
432
|
const mockCharacteristic3 = { uuid: '3' };
|
|
442
433
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
434
|
+
const promise = peripheral.discoverSomeServicesAndCharacteristicsAsync(mockServiceUuids, mockCharacteristicUuids);
|
|
435
|
+
|
|
436
|
+
// Call the callback passed to discoverServices
|
|
437
|
+
peripheral.discoverServices.mock.calls[0][1](null, mockServices);
|
|
438
|
+
|
|
439
|
+
// Call the callbacks for each service's discoverCharacteristics
|
|
440
|
+
mockServices[0].discoverCharacteristics.mock.calls[0][1](null, [mockCharacteristic1]);
|
|
441
|
+
mockServices[1].discoverCharacteristics.mock.calls[0][1](null, [mockCharacteristic2, mockCharacteristic3]);
|
|
442
|
+
|
|
443
|
+
await expect(promise).resolves.toEqual({ characteristics: [mockCharacteristic1, mockCharacteristic2, mockCharacteristic3], services: mockServices });
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test('should reject when disconnect happens during execution', async () => {
|
|
447
|
+
const mockServiceUuids = [];
|
|
448
|
+
const mockCharacteristicUuids = [];
|
|
449
|
+
|
|
450
|
+
// Override the implementation of _withDisconnectHandler to simulate disconnect during operation
|
|
451
|
+
mockNoble._withDisconnectHandler = jest.fn((id, operation) => {
|
|
452
|
+
return new Promise((resolve, reject) => {
|
|
453
|
+
// Start the operation
|
|
454
|
+
const operationPromise = operation();
|
|
455
|
+
|
|
456
|
+
// Simulate a disconnect by rejecting with a disconnect error
|
|
457
|
+
setTimeout(() => {
|
|
458
|
+
reject(new Error('Peripheral disconnected'));
|
|
459
|
+
}, 10);
|
|
460
|
+
|
|
461
|
+
return operationPromise;
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Start the async operation
|
|
466
|
+
const promise = peripheral.discoverSomeServicesAndCharacteristicsAsync(mockServiceUuids, mockCharacteristicUuids);
|
|
467
|
+
|
|
468
|
+
// Verify the promise rejects
|
|
469
|
+
await expect(promise).rejects.toEqual(expect.objectContaining({
|
|
470
|
+
message: 'Peripheral disconnected'
|
|
471
|
+
}));
|
|
472
|
+
|
|
473
|
+
// Restore original implementation
|
|
474
|
+
mockNoble._withDisconnectHandler = jest.fn((id, operation) => {
|
|
475
|
+
return new Promise((resolve, reject) => {
|
|
476
|
+
return Promise.resolve(operation())
|
|
477
|
+
.then(result => {
|
|
478
|
+
resolve(result);
|
|
479
|
+
})
|
|
480
|
+
.catch(error => {
|
|
481
|
+
reject(error);
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
});
|
|
451
485
|
});
|
|
452
486
|
});
|
|
453
487
|
|
|
454
488
|
describe('discoverAllServicesAndCharacteristics', () => {
|
|
455
489
|
beforeEach(() => {
|
|
456
|
-
peripheral.discoverSomeServicesAndCharacteristics =
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
afterEach(() => {
|
|
460
|
-
sinon.reset();
|
|
490
|
+
peripheral.discoverSomeServicesAndCharacteristics = jest.fn();
|
|
461
491
|
});
|
|
462
492
|
|
|
463
|
-
|
|
464
|
-
const callback =
|
|
493
|
+
test('should call discoverSomeServicesAndCharacteristics', () => {
|
|
494
|
+
const callback = jest.fn();
|
|
465
495
|
peripheral.discoverAllServicesAndCharacteristics(callback);
|
|
466
|
-
|
|
496
|
+
expect(peripheral.discoverSomeServicesAndCharacteristics).toHaveBeenCalledWith([], [], callback);
|
|
467
497
|
});
|
|
468
498
|
});
|
|
469
499
|
|
|
470
500
|
describe('discoverAllServicesAndCharacteristicsAsync', () => {
|
|
471
501
|
beforeEach(() => {
|
|
472
|
-
peripheral.
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
afterEach(() => {
|
|
476
|
-
sinon.reset();
|
|
502
|
+
peripheral.discoverAllServicesAndCharacteristics = jest.fn();
|
|
477
503
|
});
|
|
478
|
-
|
|
479
|
-
|
|
504
|
+
|
|
505
|
+
test('should call discoverAllServicesAndCharacteristics with a callback', async () => {
|
|
506
|
+
const promise = peripheral.discoverAllServicesAndCharacteristicsAsync();
|
|
507
|
+
|
|
508
|
+
// Find the callback that was passed to discoverAllServicesAndCharacteristics
|
|
509
|
+
const callback = peripheral.discoverAllServicesAndCharacteristics.mock.calls[0][0];
|
|
510
|
+
|
|
511
|
+
// Simulate successful callback
|
|
512
|
+
const mockServices = ['service1', 'service2'];
|
|
513
|
+
const mockCharacteristics = ['char1', 'char2'];
|
|
514
|
+
callback(null, mockServices, mockCharacteristics);
|
|
515
|
+
|
|
516
|
+
const result = await promise;
|
|
517
|
+
expect(result).toEqual({ services: mockServices, characteristics: mockCharacteristics });
|
|
518
|
+
expect(peripheral.discoverAllServicesAndCharacteristics).toHaveBeenCalledTimes(1);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
test('should reject when discoverAllServicesAndCharacteristics returns an error', async () => {
|
|
480
522
|
const promise = peripheral.discoverAllServicesAndCharacteristicsAsync();
|
|
481
|
-
|
|
482
|
-
|
|
523
|
+
|
|
524
|
+
// Find the callback that was passed to discoverAllServicesAndCharacteristics
|
|
525
|
+
const callback = peripheral.discoverAllServicesAndCharacteristics.mock.calls[0][0];
|
|
526
|
+
|
|
527
|
+
// Simulate error callback
|
|
528
|
+
const mockError = new Error('Discovery failed');
|
|
529
|
+
callback(mockError);
|
|
530
|
+
|
|
531
|
+
await expect(promise).rejects.toEqual(mockError);
|
|
532
|
+
expect(peripheral.discoverAllServicesAndCharacteristics).toHaveBeenCalledTimes(1);
|
|
483
533
|
});
|
|
484
534
|
});
|
|
485
535
|
|
|
486
536
|
describe('readHandle', () => {
|
|
487
|
-
|
|
488
|
-
mockNoble.readHandle = sinon.spy();
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
afterEach(() => {
|
|
492
|
-
sinon.reset();
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
it('should delegate to noble', () => {
|
|
537
|
+
test('should delegate to noble', () => {
|
|
496
538
|
peripheral.readHandle(mockHandle);
|
|
497
|
-
|
|
539
|
+
expect(mockNoble.readHandle).toHaveBeenCalledWith(mockId, mockHandle);
|
|
540
|
+
expect(mockNoble.readHandle).toHaveBeenCalledTimes(1);
|
|
498
541
|
});
|
|
499
542
|
|
|
500
|
-
|
|
501
|
-
const callback =
|
|
543
|
+
test('should callback', () => {
|
|
544
|
+
const callback = jest.fn();
|
|
502
545
|
|
|
503
546
|
peripheral.readHandle(mockHandle, callback);
|
|
504
547
|
peripheral.emit(`handleRead${mockHandle}`);
|
|
505
548
|
|
|
506
|
-
|
|
507
|
-
|
|
549
|
+
expect(callback).toHaveBeenCalledWith(undefined, undefined);
|
|
550
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
551
|
+
expect(mockNoble.readHandle).toHaveBeenCalledWith(mockId, mockHandle);
|
|
552
|
+
expect(mockNoble.readHandle).toHaveBeenCalledTimes(1);
|
|
508
553
|
});
|
|
509
554
|
|
|
510
|
-
|
|
511
|
-
const callback =
|
|
555
|
+
test('should callback with data', () => {
|
|
556
|
+
const callback = jest.fn();
|
|
512
557
|
|
|
513
558
|
peripheral.readHandle(mockHandle, callback);
|
|
514
559
|
peripheral.emit(`handleRead${mockHandle}`, mockData);
|
|
515
560
|
|
|
516
|
-
|
|
517
|
-
|
|
561
|
+
expect(callback).toHaveBeenCalledWith(undefined, mockData);
|
|
562
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
563
|
+
expect(mockNoble.readHandle).toHaveBeenCalledWith(mockId, mockHandle);
|
|
564
|
+
expect(mockNoble.readHandle).toHaveBeenCalledTimes(1);
|
|
518
565
|
});
|
|
519
566
|
});
|
|
520
567
|
|
|
521
568
|
describe('readHandleAsync', () => {
|
|
522
|
-
|
|
523
|
-
mockNoble.readHandle = sinon.spy();
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
afterEach(() => {
|
|
527
|
-
sinon.reset();
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
it('should delegate to noble', async () => {
|
|
569
|
+
test('should delegate to noble', async () => {
|
|
531
570
|
const promise = peripheral.readHandleAsync(mockHandle);
|
|
532
571
|
peripheral.emit(`handleRead${mockHandle}`);
|
|
533
572
|
|
|
534
|
-
|
|
535
|
-
|
|
573
|
+
await expect(promise).resolves.toBeUndefined();
|
|
574
|
+
expect(mockNoble.readHandle).toHaveBeenCalledWith(mockId, mockHandle);
|
|
575
|
+
expect(mockNoble.readHandle).toHaveBeenCalledTimes(1);
|
|
536
576
|
});
|
|
537
577
|
|
|
538
|
-
|
|
578
|
+
test('should resolve with data', async () => {
|
|
539
579
|
const promise = peripheral.readHandleAsync(mockHandle);
|
|
540
580
|
peripheral.emit(`handleRead${mockHandle}`, mockData);
|
|
541
581
|
|
|
542
|
-
|
|
543
|
-
|
|
582
|
+
await expect(promise).resolves.toEqual(mockData);
|
|
583
|
+
expect(mockNoble.readHandle).toHaveBeenCalledWith(mockId, mockHandle);
|
|
584
|
+
expect(mockNoble.readHandle).toHaveBeenCalledTimes(1);
|
|
544
585
|
});
|
|
545
586
|
});
|
|
546
587
|
|
|
547
588
|
describe('writeHandle', () => {
|
|
548
|
-
|
|
549
|
-
mockNoble.writeHandle = sinon.spy();
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
afterEach(() => {
|
|
553
|
-
sinon.reset();
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
it('should only accept data as a buffer', () => {
|
|
589
|
+
test('should only accept data as a buffer', () => {
|
|
557
590
|
const mockData = {};
|
|
558
|
-
|
|
559
|
-
|
|
591
|
+
expect(() => peripheral.writeHandle(mockHandle, mockData)).toThrow('data must be a Buffer');
|
|
592
|
+
expect(mockNoble.writeHandle).not.toHaveBeenCalled();
|
|
560
593
|
});
|
|
561
594
|
|
|
562
|
-
|
|
595
|
+
test('should delegate to noble, withoutResponse false', () => {
|
|
563
596
|
const mockData = Buffer.alloc(0);
|
|
564
597
|
peripheral.writeHandle(mockHandle, mockData, false);
|
|
565
598
|
|
|
566
|
-
|
|
599
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledWith(mockId, mockHandle, mockData, false);
|
|
600
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledTimes(1);
|
|
567
601
|
});
|
|
568
602
|
|
|
569
|
-
|
|
603
|
+
test('should delegate to noble, withoutResponse true', () => {
|
|
570
604
|
const mockData = Buffer.alloc(0);
|
|
571
605
|
peripheral.writeHandle(mockHandle, mockData, true);
|
|
572
606
|
|
|
573
|
-
|
|
607
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledWith(mockId, mockHandle, mockData, true);
|
|
608
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledTimes(1);
|
|
574
609
|
});
|
|
575
610
|
|
|
576
|
-
|
|
611
|
+
test('should callback', () => {
|
|
577
612
|
const mockData = Buffer.alloc(0);
|
|
578
|
-
const callback =
|
|
613
|
+
const callback = jest.fn();
|
|
579
614
|
|
|
580
615
|
peripheral.writeHandle(mockHandle, mockData, false, callback);
|
|
581
616
|
peripheral.emit(`handleWrite${mockHandle}`);
|
|
582
617
|
|
|
583
|
-
|
|
584
|
-
|
|
618
|
+
expect(callback).toHaveBeenCalledWith(undefined);
|
|
619
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
620
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledWith(mockId, mockHandle, mockData, false);
|
|
621
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledTimes(1);
|
|
585
622
|
});
|
|
586
623
|
});
|
|
587
624
|
|
|
588
625
|
describe('writeHandleAsync', () => {
|
|
589
|
-
|
|
590
|
-
mockNoble.writeHandle = sinon.spy();
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
afterEach(() => {
|
|
594
|
-
sinon.reset();
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
it('should only accept data as a buffer', async () => {
|
|
626
|
+
test('should only accept data as a buffer', async () => {
|
|
598
627
|
const mockData = {};
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
assert.notCalled(mockNoble.writeHandle);
|
|
628
|
+
|
|
629
|
+
await expect(peripheral.writeHandleAsync(mockHandle, mockData)).rejects.toThrow('data must be a Buffer');
|
|
630
|
+
expect(mockNoble.writeHandle).not.toHaveBeenCalled();
|
|
603
631
|
});
|
|
604
632
|
|
|
605
|
-
|
|
633
|
+
test('should delegate to noble, withoutResponse false', async () => {
|
|
606
634
|
const mockData = Buffer.alloc(0);
|
|
607
635
|
const promise = peripheral.writeHandleAsync(mockHandle, mockData, false);
|
|
608
636
|
peripheral.emit(`handleWrite${mockHandle}`);
|
|
609
637
|
|
|
610
|
-
|
|
611
|
-
|
|
638
|
+
await expect(promise).resolves.toBeUndefined();
|
|
639
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledWith(mockId, mockHandle, mockData, false);
|
|
640
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledTimes(1);
|
|
612
641
|
});
|
|
613
642
|
|
|
614
|
-
|
|
643
|
+
test('should delegate to noble, withoutResponse true', async () => {
|
|
615
644
|
const mockData = Buffer.alloc(0);
|
|
616
645
|
const promise = peripheral.writeHandleAsync(mockHandle, mockData, true);
|
|
617
646
|
peripheral.emit(`handleWrite${mockHandle}`);
|
|
618
647
|
|
|
619
|
-
|
|
620
|
-
|
|
648
|
+
await expect(promise).resolves.toBeUndefined();
|
|
649
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledWith(mockId, mockHandle, mockData, true);
|
|
650
|
+
expect(mockNoble.writeHandle).toHaveBeenCalledTimes(1);
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
describe('async commands with disconnect handling', () => {
|
|
655
|
+
// Setup a reusable helper to manage the original and mocked _withDisconnectHandler
|
|
656
|
+
let originalWithDisconnectHandler;
|
|
657
|
+
|
|
658
|
+
const setupDisconnectMock = (simulateDisconnect = true, disconnectDelay = 10) => {
|
|
659
|
+
// Save the original implementation
|
|
660
|
+
originalWithDisconnectHandler = mockNoble._withDisconnectHandler;
|
|
661
|
+
|
|
662
|
+
// Create the mock implementation
|
|
663
|
+
mockNoble._withDisconnectHandler = jest.fn((id, operation) => {
|
|
664
|
+
return new Promise((resolve, reject) => {
|
|
665
|
+
// Start the operation
|
|
666
|
+
const operationPromise = operation();
|
|
667
|
+
|
|
668
|
+
if (simulateDisconnect) {
|
|
669
|
+
// Simulate a disconnect by rejecting with a disconnect error
|
|
670
|
+
setTimeout(() => {
|
|
671
|
+
reject(new Error('Peripheral disconnected'));
|
|
672
|
+
}, disconnectDelay);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return operationPromise;
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
const restoreDisconnectMock = () => {
|
|
681
|
+
// Restore the original implementation
|
|
682
|
+
mockNoble._withDisconnectHandler = originalWithDisconnectHandler;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
afterEach(() => {
|
|
686
|
+
// Always restore after each test
|
|
687
|
+
restoreDisconnectMock();
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
test('discoverServicesAsync should handle disconnect during operation', async () => {
|
|
691
|
+
setupDisconnectMock();
|
|
692
|
+
|
|
693
|
+
const promise = peripheral.discoverServicesAsync();
|
|
694
|
+
|
|
695
|
+
await expect(promise).rejects.toEqual(expect.objectContaining({
|
|
696
|
+
message: 'Peripheral disconnected'
|
|
697
|
+
}));
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
test('updateRssiAsync should handle disconnect during operation', async () => {
|
|
701
|
+
setupDisconnectMock();
|
|
702
|
+
|
|
703
|
+
const promise = peripheral.updateRssiAsync();
|
|
704
|
+
|
|
705
|
+
await expect(promise).rejects.toEqual(expect.objectContaining({
|
|
706
|
+
message: 'Peripheral disconnected'
|
|
707
|
+
}));
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
test('readHandleAsync should handle disconnect during operation', async () => {
|
|
711
|
+
setupDisconnectMock();
|
|
712
|
+
|
|
713
|
+
const promise = peripheral.readHandleAsync(mockHandle);
|
|
714
|
+
|
|
715
|
+
await expect(promise).rejects.toEqual(expect.objectContaining({
|
|
716
|
+
message: 'Peripheral disconnected'
|
|
717
|
+
}));
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
test('writeHandleAsync should handle disconnect during operation', async () => {
|
|
721
|
+
setupDisconnectMock();
|
|
722
|
+
|
|
723
|
+
const mockData = Buffer.alloc(0);
|
|
724
|
+
const promise = peripheral.writeHandleAsync(mockHandle, mockData, false);
|
|
725
|
+
|
|
726
|
+
await expect(promise).rejects.toEqual(expect.objectContaining({
|
|
727
|
+
message: 'Peripheral disconnected'
|
|
728
|
+
}));
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
test('chain of async operations should all fail on disconnect', async () => {
|
|
732
|
+
// We'll run without disconnect initially
|
|
733
|
+
setupDisconnectMock(false);
|
|
734
|
+
|
|
735
|
+
// Setup mock responses for service discovery
|
|
736
|
+
const mockCharacteristic = { uuid: '1' };
|
|
737
|
+
const mockService = {
|
|
738
|
+
uuid: '1',
|
|
739
|
+
discoverCharacteristics: jest.fn((charUuids, callback) => {
|
|
740
|
+
callback(null, [mockCharacteristic]);
|
|
741
|
+
})
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
peripheral.discoverServices = jest.fn((uuids, callback) => {
|
|
745
|
+
callback(null, [mockService]);
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
// Start a chain of operations
|
|
749
|
+
const runOperations = async () => {
|
|
750
|
+
|
|
751
|
+
setTimeout(() => peripheral.emit('connect'), 20);
|
|
752
|
+
|
|
753
|
+
// First operation succeeds
|
|
754
|
+
await peripheral.connectAsync();
|
|
755
|
+
|
|
756
|
+
// Enable disconnection before second operation
|
|
757
|
+
setupDisconnectMock(true, 5);
|
|
758
|
+
|
|
759
|
+
// This should fail with disconnect
|
|
760
|
+
await peripheral.discoverAllServicesAndCharacteristics();
|
|
761
|
+
|
|
762
|
+
// These should never be reached
|
|
763
|
+
await peripheral.readHandleAsync(mockHandle);
|
|
764
|
+
await peripheral.writeHandleAsync(mockHandle, Buffer.alloc(0));
|
|
765
|
+
|
|
766
|
+
return 'completed';
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
await expect(runOperations()).rejects.toEqual(expect.objectContaining({
|
|
770
|
+
message: 'Peripheral disconnected'
|
|
771
|
+
}));
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
test('multiple concurrent async operations should all fail on disconnect', async () => {
|
|
775
|
+
setupDisconnectMock(true, 5);
|
|
776
|
+
|
|
777
|
+
// Start multiple operations at the same time
|
|
778
|
+
const promises = [
|
|
779
|
+
peripheral.updateRssiAsync(),
|
|
780
|
+
peripheral.discoverServicesAsync(),
|
|
781
|
+
peripheral.readHandleAsync(mockHandle),
|
|
782
|
+
peripheral.writeHandleAsync(mockHandle, Buffer.alloc(0))
|
|
783
|
+
];
|
|
784
|
+
|
|
785
|
+
// All operations should fail with the same disconnect error
|
|
786
|
+
await Promise.all(promises.map(promise =>
|
|
787
|
+
expect(promise).rejects.toEqual(expect.objectContaining({
|
|
788
|
+
message: 'Peripheral disconnected'
|
|
789
|
+
}))
|
|
790
|
+
));
|
|
621
791
|
});
|
|
622
792
|
});
|
|
623
|
-
});
|
|
793
|
+
});
|