react-native-ble-nitro 1.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +298 -0
- package/android/build.gradle +55 -0
- package/android/src/main/AndroidManifest.xml +23 -0
- package/android/src/main/kotlin/co/zyke/ble/BleNitroBleManager.kt +651 -0
- package/android/src/main/kotlin/co/zyke/ble/BleNitroPackage.kt +37 -0
- package/ios/BleNitro.podspec +37 -0
- package/ios/BleNitroBleManager.swift +509 -0
- package/ios/BleNitroModule.swift +31 -0
- package/lib/BleManagerCompatFactory.d.ts +53 -0
- package/lib/BleManagerCompatFactory.js +191 -0
- package/lib/BleManagerFactory.d.ts +12 -0
- package/lib/BleManagerFactory.js +22 -0
- package/lib/compatibility/constants.d.ts +49 -0
- package/lib/compatibility/constants.js +50 -0
- package/lib/compatibility/deviceWrapper.d.ts +99 -0
- package/lib/compatibility/deviceWrapper.js +259 -0
- package/lib/compatibility/enums.d.ts +43 -0
- package/lib/compatibility/enums.js +124 -0
- package/lib/compatibility/index.d.ts +11 -0
- package/lib/compatibility/index.js +12 -0
- package/lib/compatibility/serviceData.d.ts +51 -0
- package/lib/compatibility/serviceData.js +70 -0
- package/lib/errors/BleError.d.ts +59 -0
- package/lib/errors/BleError.js +120 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +12 -0
- package/lib/specs/BleManager.nitro.d.ts +36 -0
- package/lib/specs/BleManager.nitro.js +1 -0
- package/lib/specs/Characteristic.nitro.d.ts +26 -0
- package/lib/specs/Characteristic.nitro.js +1 -0
- package/lib/specs/Descriptor.nitro.d.ts +17 -0
- package/lib/specs/Descriptor.nitro.js +1 -0
- package/lib/specs/Device.nitro.d.ts +37 -0
- package/lib/specs/Device.nitro.js +1 -0
- package/lib/specs/Service.nitro.d.ts +19 -0
- package/lib/specs/Service.nitro.js +1 -0
- package/lib/specs/types.d.ts +228 -0
- package/lib/specs/types.js +146 -0
- package/lib/utils/base64.d.ts +25 -0
- package/lib/utils/base64.js +80 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +2 -0
- package/lib/utils/uuid.d.ts +9 -0
- package/lib/utils/uuid.js +37 -0
- package/nitro.json +15 -0
- package/package.json +102 -0
- package/plugin/build/index.d.ts +28 -0
- package/plugin/build/index.js +29 -0
- package/plugin/build/withBleNitro.d.ts +31 -0
- package/plugin/build/withBleNitro.js +87 -0
- package/react-native.config.js +13 -0
- package/src/BleManagerCompatFactory.ts +373 -0
- package/src/BleManagerFactory.ts +30 -0
- package/src/__tests__/BleManager.test.ts +327 -0
- package/src/__tests__/compatibility/deviceWrapper.test.ts +563 -0
- package/src/__tests__/compatibility/enums.test.ts +254 -0
- package/src/compatibility/constants.ts +71 -0
- package/src/compatibility/deviceWrapper.ts +427 -0
- package/src/compatibility/enums.ts +160 -0
- package/src/compatibility/index.ts +24 -0
- package/src/compatibility/serviceData.ts +85 -0
- package/src/errors/BleError.ts +193 -0
- package/src/index.ts +30 -0
- package/src/specs/BleManager.nitro.ts +152 -0
- package/src/specs/Characteristic.nitro.ts +61 -0
- package/src/specs/Descriptor.nitro.ts +28 -0
- package/src/specs/Device.nitro.ts +104 -0
- package/src/specs/Service.nitro.ts +64 -0
- package/src/specs/types.ts +259 -0
- package/src/utils/base64.ts +80 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/uuid.ts +45 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* deviceWrapper.test.ts
|
|
3
|
+
* React Native BLE Nitro - Device Wrapper Tests
|
|
4
|
+
* Copyright © 2025 Zyke (https://zyke.co)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { DeviceWrapper, ServiceWrapper, CharacteristicWrapper, DescriptorWrapper } from '../../compatibility/deviceWrapper';
|
|
8
|
+
import { ConnectionPriority, CharacteristicSubscriptionType } from '../../specs/types';
|
|
9
|
+
|
|
10
|
+
// Mock Nitro Device
|
|
11
|
+
const createMockNitroDevice = () => ({
|
|
12
|
+
id: 'test-device-id',
|
|
13
|
+
deviceName: 'Test Device',
|
|
14
|
+
rssi: -50,
|
|
15
|
+
mtu: 247,
|
|
16
|
+
manufacturerData: 'AQIDBA==', // [1, 2, 3, 4] in base64
|
|
17
|
+
serviceData: [
|
|
18
|
+
{ uuid: '180f', data: 'ZA==' }, // Battery service with value 100
|
|
19
|
+
{ uuid: '1234', data: 'dGVzdA==' } // Custom service with "test"
|
|
20
|
+
],
|
|
21
|
+
serviceUUIDs: ['180f', '1234-5678-9abc-def0-123456789abc'],
|
|
22
|
+
localName: 'Local Test Device',
|
|
23
|
+
txPowerLevel: 4,
|
|
24
|
+
solicitedServiceUUIDs: ['abcd'],
|
|
25
|
+
isConnectable: true,
|
|
26
|
+
overflowServiceUUIDs: ['efgh'],
|
|
27
|
+
rawScanRecord: 'AQIDBAU=',
|
|
28
|
+
|
|
29
|
+
// Mock methods
|
|
30
|
+
requestConnectionPriority: jest.fn().mockResolvedValue({}),
|
|
31
|
+
readRSSI: jest.fn().mockResolvedValue({}),
|
|
32
|
+
requestMTU: jest.fn().mockResolvedValue({}),
|
|
33
|
+
connect: jest.fn().mockResolvedValue({}),
|
|
34
|
+
cancelConnection: jest.fn().mockResolvedValue({}),
|
|
35
|
+
isConnected: jest.fn().mockResolvedValue(true),
|
|
36
|
+
onDisconnected: jest.fn().mockReturnValue({ remove: jest.fn() }),
|
|
37
|
+
discoverAllServicesAndCharacteristics: jest.fn().mockResolvedValue({}),
|
|
38
|
+
services: jest.fn().mockResolvedValue([]),
|
|
39
|
+
characteristicsForService: jest.fn().mockResolvedValue([]),
|
|
40
|
+
readCharacteristicForService: jest.fn().mockResolvedValue({}),
|
|
41
|
+
writeCharacteristicWithResponseForService: jest.fn().mockResolvedValue({}),
|
|
42
|
+
writeCharacteristicWithoutResponseForService: jest.fn().mockResolvedValue({}),
|
|
43
|
+
monitorCharacteristicForService: jest.fn().mockReturnValue({ remove: jest.fn() }),
|
|
44
|
+
descriptorsForService: jest.fn().mockResolvedValue([]),
|
|
45
|
+
readDescriptorForService: jest.fn().mockResolvedValue({}),
|
|
46
|
+
writeDescriptorForService: jest.fn().mockResolvedValue({})
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('DeviceWrapper', () => {
|
|
50
|
+
let mockNitroDevice: ReturnType<typeof createMockNitroDevice>;
|
|
51
|
+
let deviceWrapper: DeviceWrapper;
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
mockNitroDevice = createMockNitroDevice();
|
|
55
|
+
deviceWrapper = new DeviceWrapper(mockNitroDevice);
|
|
56
|
+
jest.clearAllMocks();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('Property Mapping', () => {
|
|
60
|
+
it('should map device identification properties correctly', () => {
|
|
61
|
+
expect(deviceWrapper.id).toBe('test-device-id');
|
|
62
|
+
expect(deviceWrapper.name).toBe('Test Device'); // Maps from deviceName
|
|
63
|
+
expect(deviceWrapper.rssi).toBe(-50);
|
|
64
|
+
expect(deviceWrapper.mtu).toBe(247);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should handle null device name', () => {
|
|
68
|
+
mockNitroDevice.deviceName = null;
|
|
69
|
+
const wrapper = new DeviceWrapper(mockNitroDevice);
|
|
70
|
+
expect(wrapper.name).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should map advertisement data properties', () => {
|
|
74
|
+
expect(deviceWrapper.manufacturerData).toBe('AQIDBA==');
|
|
75
|
+
expect(deviceWrapper.rawScanRecord).toBe('AQIDBAU=');
|
|
76
|
+
expect(deviceWrapper.localName).toBe('Local Test Device');
|
|
77
|
+
expect(deviceWrapper.txPowerLevel).toBe(4);
|
|
78
|
+
expect(deviceWrapper.isConnectable).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should map service-related properties', () => {
|
|
82
|
+
expect(deviceWrapper.serviceUUIDs).toEqual(['180f', '1234-5678-9abc-def0-123456789abc']);
|
|
83
|
+
expect(deviceWrapper.solicitedServiceUUIDs).toEqual(['abcd']);
|
|
84
|
+
expect(deviceWrapper.overflowServiceUUIDs).toEqual(['efgh']);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should convert service data array to map format', () => {
|
|
88
|
+
const serviceData = deviceWrapper.serviceData;
|
|
89
|
+
expect(serviceData).toEqual({
|
|
90
|
+
'180f': 'ZA==',
|
|
91
|
+
'1234': 'dGVzdA=='
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should handle null service data', () => {
|
|
96
|
+
mockNitroDevice.serviceData = null;
|
|
97
|
+
const wrapper = new DeviceWrapper(mockNitroDevice);
|
|
98
|
+
expect(wrapper.serviceData).toBeNull();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('Connection Management', () => {
|
|
103
|
+
it('should request connection priority', async () => {
|
|
104
|
+
mockNitroDevice.requestConnectionPriority.mockResolvedValue(mockNitroDevice);
|
|
105
|
+
|
|
106
|
+
const result = await deviceWrapper.requestConnectionPriority(
|
|
107
|
+
ConnectionPriority.High,
|
|
108
|
+
'transaction-123'
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(result).toBeInstanceOf(DeviceWrapper);
|
|
112
|
+
expect(mockNitroDevice.requestConnectionPriority).toHaveBeenCalledWith(
|
|
113
|
+
ConnectionPriority.High,
|
|
114
|
+
'transaction-123'
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should read RSSI', async () => {
|
|
119
|
+
const updatedDevice = { ...mockNitroDevice, rssi: -45 };
|
|
120
|
+
mockNitroDevice.readRSSI.mockResolvedValue(updatedDevice);
|
|
121
|
+
|
|
122
|
+
const result = await deviceWrapper.readRSSI('transaction-456');
|
|
123
|
+
|
|
124
|
+
expect(result).toBeInstanceOf(DeviceWrapper);
|
|
125
|
+
expect(mockNitroDevice.readRSSI).toHaveBeenCalledWith('transaction-456');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should request MTU change', async () => {
|
|
129
|
+
const updatedDevice = { ...mockNitroDevice, mtu: 512 };
|
|
130
|
+
mockNitroDevice.requestMTU.mockResolvedValue(updatedDevice);
|
|
131
|
+
|
|
132
|
+
const result = await deviceWrapper.requestMTU(512, 'mtu-transaction');
|
|
133
|
+
|
|
134
|
+
expect(result).toBeInstanceOf(DeviceWrapper);
|
|
135
|
+
expect(mockNitroDevice.requestMTU).toHaveBeenCalledWith(512, 'mtu-transaction');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should connect with default options', async () => {
|
|
139
|
+
mockNitroDevice.connect.mockResolvedValue(mockNitroDevice);
|
|
140
|
+
|
|
141
|
+
const result = await deviceWrapper.connect();
|
|
142
|
+
|
|
143
|
+
expect(result).toBeInstanceOf(DeviceWrapper);
|
|
144
|
+
expect(mockNitroDevice.connect).toHaveBeenCalledWith({
|
|
145
|
+
autoConnect: false,
|
|
146
|
+
requestMTU: 23,
|
|
147
|
+
timeout: 0
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should connect with custom options', async () => {
|
|
152
|
+
mockNitroDevice.connect.mockResolvedValue(mockNitroDevice);
|
|
153
|
+
|
|
154
|
+
const result = await deviceWrapper.connect({
|
|
155
|
+
autoConnect: true,
|
|
156
|
+
requestMTU: 247,
|
|
157
|
+
timeout: 5000
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
expect(result).toBeInstanceOf(DeviceWrapper);
|
|
161
|
+
expect(mockNitroDevice.connect).toHaveBeenCalledWith({
|
|
162
|
+
autoConnect: true,
|
|
163
|
+
requestMTU: 247,
|
|
164
|
+
timeout: 5000
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should cancel connection', async () => {
|
|
169
|
+
mockNitroDevice.cancelConnection.mockResolvedValue(mockNitroDevice);
|
|
170
|
+
|
|
171
|
+
const result = await deviceWrapper.cancelConnection();
|
|
172
|
+
|
|
173
|
+
expect(result).toBeInstanceOf(DeviceWrapper);
|
|
174
|
+
expect(mockNitroDevice.cancelConnection).toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should check connection status', async () => {
|
|
178
|
+
mockNitroDevice.isConnected.mockResolvedValue(true);
|
|
179
|
+
|
|
180
|
+
const result = await deviceWrapper.isConnected();
|
|
181
|
+
|
|
182
|
+
expect(result).toBe(true);
|
|
183
|
+
expect(mockNitroDevice.isConnected).toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should handle disconnection listener', () => {
|
|
187
|
+
const listener = jest.fn();
|
|
188
|
+
const mockSubscription = { remove: jest.fn() };
|
|
189
|
+
mockNitroDevice.onDisconnected.mockReturnValue(mockSubscription);
|
|
190
|
+
|
|
191
|
+
const subscription = deviceWrapper.onDisconnected(listener);
|
|
192
|
+
|
|
193
|
+
expect(subscription).toBe(mockSubscription);
|
|
194
|
+
expect(mockNitroDevice.onDisconnected).toHaveBeenCalled();
|
|
195
|
+
|
|
196
|
+
// Verify the listener wrapper is called correctly
|
|
197
|
+
const callArgs = mockNitroDevice.onDisconnected.mock.calls[0][0];
|
|
198
|
+
const mockError = new Error('Disconnected');
|
|
199
|
+
const mockDevice = { ...mockNitroDevice };
|
|
200
|
+
|
|
201
|
+
callArgs(mockError, mockDevice);
|
|
202
|
+
expect(listener).toHaveBeenCalledWith(mockError, expect.any(DeviceWrapper));
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('Service Discovery', () => {
|
|
207
|
+
it('should discover all services and characteristics', async () => {
|
|
208
|
+
mockNitroDevice.discoverAllServicesAndCharacteristics.mockResolvedValue(mockNitroDevice);
|
|
209
|
+
|
|
210
|
+
const result = await deviceWrapper.discoverAllServicesAndCharacteristics('discovery-tx');
|
|
211
|
+
|
|
212
|
+
expect(result).toBeInstanceOf(DeviceWrapper);
|
|
213
|
+
expect(mockNitroDevice.discoverAllServicesAndCharacteristics).toHaveBeenCalledWith('discovery-tx');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should get services list', async () => {
|
|
217
|
+
const mockServices = [
|
|
218
|
+
{ id: 1, uuid: '180f', deviceID: 'test-device-id', isPrimary: true }
|
|
219
|
+
];
|
|
220
|
+
mockNitroDevice.services.mockResolvedValue(mockServices);
|
|
221
|
+
|
|
222
|
+
const result = await deviceWrapper.services();
|
|
223
|
+
|
|
224
|
+
expect(result).toHaveLength(1);
|
|
225
|
+
expect(result[0]).toBeInstanceOf(ServiceWrapper);
|
|
226
|
+
expect(mockNitroDevice.services).toHaveBeenCalled();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe('Characteristic Operations', () => {
|
|
231
|
+
const serviceUUID = '180f';
|
|
232
|
+
const characteristicUUID = '2a19';
|
|
233
|
+
|
|
234
|
+
it('should get characteristics for service', async () => {
|
|
235
|
+
const mockCharacteristics = [
|
|
236
|
+
{
|
|
237
|
+
id: 1,
|
|
238
|
+
uuid: characteristicUUID,
|
|
239
|
+
serviceID: 1,
|
|
240
|
+
serviceUUID,
|
|
241
|
+
deviceID: 'test-device-id',
|
|
242
|
+
isReadable: true,
|
|
243
|
+
isWritableWithResponse: false,
|
|
244
|
+
isWritableWithoutResponse: false,
|
|
245
|
+
isNotifiable: true,
|
|
246
|
+
isNotifying: false,
|
|
247
|
+
isIndicatable: false,
|
|
248
|
+
value: null
|
|
249
|
+
}
|
|
250
|
+
];
|
|
251
|
+
mockNitroDevice.characteristicsForService.mockResolvedValue(mockCharacteristics);
|
|
252
|
+
|
|
253
|
+
const result = await deviceWrapper.characteristicsForService(serviceUUID);
|
|
254
|
+
|
|
255
|
+
expect(result).toHaveLength(1);
|
|
256
|
+
expect(result[0]).toBeInstanceOf(CharacteristicWrapper);
|
|
257
|
+
expect(mockNitroDevice.characteristicsForService).toHaveBeenCalledWith(serviceUUID);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should read characteristic', async () => {
|
|
261
|
+
const mockCharacteristic = {
|
|
262
|
+
id: 1,
|
|
263
|
+
uuid: characteristicUUID,
|
|
264
|
+
value: 'dGVzdA=='
|
|
265
|
+
};
|
|
266
|
+
mockNitroDevice.readCharacteristicForService.mockResolvedValue(mockCharacteristic);
|
|
267
|
+
|
|
268
|
+
const result = await deviceWrapper.readCharacteristicForService(
|
|
269
|
+
serviceUUID,
|
|
270
|
+
characteristicUUID,
|
|
271
|
+
'read-tx'
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
expect(result).toBeInstanceOf(CharacteristicWrapper);
|
|
275
|
+
expect(mockNitroDevice.readCharacteristicForService).toHaveBeenCalledWith(
|
|
276
|
+
serviceUUID,
|
|
277
|
+
characteristicUUID,
|
|
278
|
+
'read-tx'
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should write characteristic with response', async () => {
|
|
283
|
+
const value = 'dGVzdCB2YWx1ZQ==';
|
|
284
|
+
const mockCharacteristic = {
|
|
285
|
+
id: 1,
|
|
286
|
+
uuid: characteristicUUID,
|
|
287
|
+
value
|
|
288
|
+
};
|
|
289
|
+
mockNitroDevice.writeCharacteristicWithResponseForService.mockResolvedValue(mockCharacteristic);
|
|
290
|
+
|
|
291
|
+
const result = await deviceWrapper.writeCharacteristicWithResponseForService(
|
|
292
|
+
serviceUUID,
|
|
293
|
+
characteristicUUID,
|
|
294
|
+
value,
|
|
295
|
+
'write-tx'
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
expect(result).toBeInstanceOf(CharacteristicWrapper);
|
|
299
|
+
expect(mockNitroDevice.writeCharacteristicWithResponseForService).toHaveBeenCalledWith(
|
|
300
|
+
serviceUUID,
|
|
301
|
+
characteristicUUID,
|
|
302
|
+
value,
|
|
303
|
+
'write-tx'
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should write characteristic without response', async () => {
|
|
308
|
+
const value = 'dGVzdA==';
|
|
309
|
+
const mockCharacteristic = {
|
|
310
|
+
id: 1,
|
|
311
|
+
uuid: characteristicUUID,
|
|
312
|
+
value
|
|
313
|
+
};
|
|
314
|
+
mockNitroDevice.writeCharacteristicWithoutResponseForService.mockResolvedValue(mockCharacteristic);
|
|
315
|
+
|
|
316
|
+
const result = await deviceWrapper.writeCharacteristicWithoutResponseForService(
|
|
317
|
+
serviceUUID,
|
|
318
|
+
characteristicUUID,
|
|
319
|
+
value
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
expect(result).toBeInstanceOf(CharacteristicWrapper);
|
|
323
|
+
expect(mockNitroDevice.writeCharacteristicWithoutResponseForService).toHaveBeenCalledWith(
|
|
324
|
+
serviceUUID,
|
|
325
|
+
characteristicUUID,
|
|
326
|
+
value,
|
|
327
|
+
undefined
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should monitor characteristic for changes', () => {
|
|
332
|
+
const listener = jest.fn();
|
|
333
|
+
const mockSubscription = { remove: jest.fn() };
|
|
334
|
+
mockNitroDevice.monitorCharacteristicForService.mockReturnValue(mockSubscription);
|
|
335
|
+
|
|
336
|
+
const result = deviceWrapper.monitorCharacteristicForService(
|
|
337
|
+
serviceUUID,
|
|
338
|
+
characteristicUUID,
|
|
339
|
+
listener,
|
|
340
|
+
'monitor-tx',
|
|
341
|
+
'notification'
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
expect(result).toBe(mockSubscription);
|
|
345
|
+
expect(mockNitroDevice.monitorCharacteristicForService).toHaveBeenCalledWith(
|
|
346
|
+
serviceUUID,
|
|
347
|
+
characteristicUUID,
|
|
348
|
+
expect.any(Function),
|
|
349
|
+
'monitor-tx',
|
|
350
|
+
CharacteristicSubscriptionType.Notification
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
// Test the listener wrapper
|
|
354
|
+
const callArgs = mockNitroDevice.monitorCharacteristicForService.mock.calls[0][2];
|
|
355
|
+
const mockError = null;
|
|
356
|
+
const mockCharacteristic = { id: 1, uuid: characteristicUUID };
|
|
357
|
+
|
|
358
|
+
callArgs(mockError, mockCharacteristic);
|
|
359
|
+
expect(listener).toHaveBeenCalledWith(mockError, expect.any(CharacteristicWrapper));
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should monitor characteristic without subscription type', () => {
|
|
363
|
+
const listener = jest.fn();
|
|
364
|
+
const mockSubscription = { remove: jest.fn() };
|
|
365
|
+
mockNitroDevice.monitorCharacteristicForService.mockReturnValue(mockSubscription);
|
|
366
|
+
|
|
367
|
+
deviceWrapper.monitorCharacteristicForService(
|
|
368
|
+
serviceUUID,
|
|
369
|
+
characteristicUUID,
|
|
370
|
+
listener
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
expect(mockNitroDevice.monitorCharacteristicForService).toHaveBeenCalledWith(
|
|
374
|
+
serviceUUID,
|
|
375
|
+
characteristicUUID,
|
|
376
|
+
expect.any(Function),
|
|
377
|
+
undefined,
|
|
378
|
+
undefined
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('Descriptor Operations', () => {
|
|
384
|
+
const serviceUUID = '180f';
|
|
385
|
+
const characteristicUUID = '2a19';
|
|
386
|
+
const descriptorUUID = '2902';
|
|
387
|
+
|
|
388
|
+
it('should get descriptors for characteristic', async () => {
|
|
389
|
+
const mockDescriptors = [
|
|
390
|
+
{
|
|
391
|
+
id: 1,
|
|
392
|
+
uuid: descriptorUUID,
|
|
393
|
+
characteristicID: 1,
|
|
394
|
+
characteristicUUID,
|
|
395
|
+
serviceID: 1,
|
|
396
|
+
serviceUUID,
|
|
397
|
+
deviceID: 'test-device-id',
|
|
398
|
+
value: null
|
|
399
|
+
}
|
|
400
|
+
];
|
|
401
|
+
mockNitroDevice.descriptorsForService.mockResolvedValue(mockDescriptors);
|
|
402
|
+
|
|
403
|
+
const result = await deviceWrapper.descriptorsForService(serviceUUID, characteristicUUID);
|
|
404
|
+
|
|
405
|
+
expect(result).toHaveLength(1);
|
|
406
|
+
expect(result[0]).toBeInstanceOf(DescriptorWrapper);
|
|
407
|
+
expect(mockNitroDevice.descriptorsForService).toHaveBeenCalledWith(
|
|
408
|
+
serviceUUID,
|
|
409
|
+
characteristicUUID
|
|
410
|
+
);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should read descriptor', async () => {
|
|
414
|
+
const mockDescriptor = {
|
|
415
|
+
id: 1,
|
|
416
|
+
uuid: descriptorUUID,
|
|
417
|
+
value: 'AQA=' // [1, 0] in base64
|
|
418
|
+
};
|
|
419
|
+
mockNitroDevice.readDescriptorForService.mockResolvedValue(mockDescriptor);
|
|
420
|
+
|
|
421
|
+
const result = await deviceWrapper.readDescriptorForService(
|
|
422
|
+
serviceUUID,
|
|
423
|
+
characteristicUUID,
|
|
424
|
+
descriptorUUID,
|
|
425
|
+
'descriptor-read-tx'
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
expect(result).toBeInstanceOf(DescriptorWrapper);
|
|
429
|
+
expect(mockNitroDevice.readDescriptorForService).toHaveBeenCalledWith(
|
|
430
|
+
serviceUUID,
|
|
431
|
+
characteristicUUID,
|
|
432
|
+
descriptorUUID,
|
|
433
|
+
'descriptor-read-tx'
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should write descriptor', async () => {
|
|
438
|
+
const value = 'AQA=';
|
|
439
|
+
const mockDescriptor = {
|
|
440
|
+
id: 1,
|
|
441
|
+
uuid: descriptorUUID,
|
|
442
|
+
value
|
|
443
|
+
};
|
|
444
|
+
mockNitroDevice.writeDescriptorForService.mockResolvedValue(mockDescriptor);
|
|
445
|
+
|
|
446
|
+
const result = await deviceWrapper.writeDescriptorForService(
|
|
447
|
+
serviceUUID,
|
|
448
|
+
characteristicUUID,
|
|
449
|
+
descriptorUUID,
|
|
450
|
+
value,
|
|
451
|
+
'descriptor-write-tx'
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
expect(result).toBeInstanceOf(DescriptorWrapper);
|
|
455
|
+
expect(mockNitroDevice.writeDescriptorForService).toHaveBeenCalledWith(
|
|
456
|
+
serviceUUID,
|
|
457
|
+
characteristicUUID,
|
|
458
|
+
descriptorUUID,
|
|
459
|
+
value,
|
|
460
|
+
'descriptor-write-tx'
|
|
461
|
+
);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
describe('ServiceWrapper', () => {
|
|
467
|
+
let mockService: any;
|
|
468
|
+
let mockNitroDevice: any;
|
|
469
|
+
let serviceWrapper: ServiceWrapper;
|
|
470
|
+
|
|
471
|
+
beforeEach(() => {
|
|
472
|
+
mockService = {
|
|
473
|
+
id: 1,
|
|
474
|
+
uuid: '180f',
|
|
475
|
+
deviceID: 'test-device',
|
|
476
|
+
isPrimary: true
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
mockNitroDevice = createMockNitroDevice();
|
|
480
|
+
serviceWrapper = new ServiceWrapper(mockService, mockNitroDevice);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should expose service properties', () => {
|
|
484
|
+
expect(serviceWrapper.id).toBe(1);
|
|
485
|
+
expect(serviceWrapper.uuid).toBe('180f');
|
|
486
|
+
expect(serviceWrapper.deviceID).toBe('test-device');
|
|
487
|
+
expect(serviceWrapper.isPrimary).toBe(true);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('should delegate characteristics method to device', async () => {
|
|
491
|
+
mockNitroDevice.characteristicsForService.mockResolvedValue([]);
|
|
492
|
+
|
|
493
|
+
await serviceWrapper.characteristics();
|
|
494
|
+
|
|
495
|
+
expect(mockNitroDevice.characteristicsForService).toHaveBeenCalledWith('180f');
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should delegate read characteristic method to device', async () => {
|
|
499
|
+
const characteristicUUID = '2a19';
|
|
500
|
+
mockNitroDevice.readCharacteristicForService.mockResolvedValue({});
|
|
501
|
+
|
|
502
|
+
await serviceWrapper.readCharacteristic(characteristicUUID, 'tx-123');
|
|
503
|
+
|
|
504
|
+
expect(mockNitroDevice.readCharacteristicForService).toHaveBeenCalledWith(
|
|
505
|
+
'180f',
|
|
506
|
+
characteristicUUID,
|
|
507
|
+
'tx-123'
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
describe('CharacteristicWrapper', () => {
|
|
513
|
+
let mockCharacteristic: any;
|
|
514
|
+
let mockNitroDevice: any;
|
|
515
|
+
let characteristicWrapper: CharacteristicWrapper;
|
|
516
|
+
|
|
517
|
+
beforeEach(() => {
|
|
518
|
+
mockCharacteristic = {
|
|
519
|
+
id: 1,
|
|
520
|
+
uuid: '2a19',
|
|
521
|
+
serviceID: 1,
|
|
522
|
+
serviceUUID: '180f',
|
|
523
|
+
deviceID: 'test-device',
|
|
524
|
+
isReadable: true,
|
|
525
|
+
isWritableWithResponse: true,
|
|
526
|
+
isWritableWithoutResponse: false,
|
|
527
|
+
isNotifiable: true,
|
|
528
|
+
isNotifying: false,
|
|
529
|
+
isIndicatable: false,
|
|
530
|
+
value: 'dGVzdA=='
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
mockNitroDevice = createMockNitroDevice();
|
|
534
|
+
characteristicWrapper = new CharacteristicWrapper(mockCharacteristic, mockNitroDevice);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('should expose characteristic properties', () => {
|
|
538
|
+
expect(characteristicWrapper.id).toBe(1);
|
|
539
|
+
expect(characteristicWrapper.uuid).toBe('2a19');
|
|
540
|
+
expect(characteristicWrapper.serviceID).toBe(1);
|
|
541
|
+
expect(characteristicWrapper.serviceUUID).toBe('180f');
|
|
542
|
+
expect(characteristicWrapper.deviceID).toBe('test-device');
|
|
543
|
+
expect(characteristicWrapper.isReadable).toBe(true);
|
|
544
|
+
expect(characteristicWrapper.isWritableWithResponse).toBe(true);
|
|
545
|
+
expect(characteristicWrapper.isWritableWithoutResponse).toBe(false);
|
|
546
|
+
expect(characteristicWrapper.isNotifiable).toBe(true);
|
|
547
|
+
expect(characteristicWrapper.isNotifying).toBe(false);
|
|
548
|
+
expect(characteristicWrapper.isIndicatable).toBe(false);
|
|
549
|
+
expect(characteristicWrapper.value).toBe('dGVzdA==');
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('should delegate read method to device', async () => {
|
|
553
|
+
mockNitroDevice.readCharacteristicForService.mockResolvedValue(mockCharacteristic);
|
|
554
|
+
|
|
555
|
+
await characteristicWrapper.read('read-tx');
|
|
556
|
+
|
|
557
|
+
expect(mockNitroDevice.readCharacteristicForService).toHaveBeenCalledWith(
|
|
558
|
+
'180f',
|
|
559
|
+
'2a19',
|
|
560
|
+
'read-tx'
|
|
561
|
+
);
|
|
562
|
+
});
|
|
563
|
+
});
|