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,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BleManager.test.ts
|
|
3
|
+
* React Native BLE Nitro - Unit Tests
|
|
4
|
+
* Copyright © 2025 Zyke (https://zyke.co)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { BleManager } from '../index';
|
|
8
|
+
import { State, LogLevel } from '../specs/types';
|
|
9
|
+
|
|
10
|
+
// Create a mock Nitro manager
|
|
11
|
+
const mockNitroManager = {
|
|
12
|
+
destroy: jest.fn().mockResolvedValue(undefined),
|
|
13
|
+
setLogLevel: jest.fn().mockResolvedValue(LogLevel.Info),
|
|
14
|
+
logLevel: jest.fn().mockResolvedValue(LogLevel.Info),
|
|
15
|
+
state: jest.fn().mockResolvedValue(State.PoweredOn),
|
|
16
|
+
onStateChange: jest.fn().mockReturnValue({ remove: jest.fn() }),
|
|
17
|
+
startDeviceScan: jest.fn().mockResolvedValue(undefined),
|
|
18
|
+
stopDeviceScan: jest.fn().mockResolvedValue(undefined),
|
|
19
|
+
connectToDevice: jest.fn().mockResolvedValue({
|
|
20
|
+
id: 'test-device',
|
|
21
|
+
name: 'Test Device',
|
|
22
|
+
deviceName: 'Test Device',
|
|
23
|
+
rssi: -50,
|
|
24
|
+
mtu: 23
|
|
25
|
+
}),
|
|
26
|
+
cancelDeviceConnection: jest.fn().mockResolvedValue({
|
|
27
|
+
id: 'test-device',
|
|
28
|
+
name: 'Test Device',
|
|
29
|
+
deviceName: 'Test Device'
|
|
30
|
+
}),
|
|
31
|
+
isDeviceConnected: jest.fn().mockResolvedValue(true),
|
|
32
|
+
discoverAllServicesAndCharacteristicsForDevice: jest.fn().mockResolvedValue({
|
|
33
|
+
id: 'test-device',
|
|
34
|
+
name: 'Test Device',
|
|
35
|
+
deviceName: 'Test Device'
|
|
36
|
+
}),
|
|
37
|
+
servicesForDevice: jest.fn().mockResolvedValue([
|
|
38
|
+
{
|
|
39
|
+
id: 1,
|
|
40
|
+
uuid: '12345678-1234-5678-9abc-123456789abc',
|
|
41
|
+
deviceID: 'test-device',
|
|
42
|
+
isPrimary: true
|
|
43
|
+
}
|
|
44
|
+
]),
|
|
45
|
+
characteristicsForDevice: jest.fn().mockResolvedValue([
|
|
46
|
+
{
|
|
47
|
+
id: 1,
|
|
48
|
+
uuid: '87654321-4321-8765-cba9-987654321cba',
|
|
49
|
+
serviceID: 1,
|
|
50
|
+
serviceUUID: '12345678-1234-5678-9abc-123456789abc',
|
|
51
|
+
deviceID: 'test-device',
|
|
52
|
+
isReadable: true,
|
|
53
|
+
isWritableWithResponse: true,
|
|
54
|
+
isWritableWithoutResponse: false,
|
|
55
|
+
isNotifiable: true,
|
|
56
|
+
isNotifying: false,
|
|
57
|
+
isIndicatable: false,
|
|
58
|
+
value: null
|
|
59
|
+
}
|
|
60
|
+
]),
|
|
61
|
+
readCharacteristicForDevice: jest.fn().mockResolvedValue({
|
|
62
|
+
id: 1,
|
|
63
|
+
uuid: '87654321-4321-8765-cba9-987654321cba',
|
|
64
|
+
value: 'dGVzdCB2YWx1ZQ==' // "test value" in base64
|
|
65
|
+
}),
|
|
66
|
+
writeCharacteristicWithResponseForDevice: jest.fn().mockResolvedValue({
|
|
67
|
+
id: 1,
|
|
68
|
+
uuid: '87654321-4321-8765-cba9-987654321cba',
|
|
69
|
+
value: 'dGVzdCB2YWx1ZQ=='
|
|
70
|
+
}),
|
|
71
|
+
monitorCharacteristicForDevice: jest.fn().mockReturnValue({ remove: jest.fn() }),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Mock the Nitro module
|
|
75
|
+
jest.mock('react-native-nitro-modules', () => ({
|
|
76
|
+
NitroModules: {
|
|
77
|
+
createHybridObject: jest.fn(() => mockNitroManager),
|
|
78
|
+
},
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
// Mock the BleManagerFactory
|
|
82
|
+
jest.mock('../BleManagerFactory', () => ({
|
|
83
|
+
createBleManager: jest.fn(() => mockNitroManager),
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
describe('BleManager', () => {
|
|
87
|
+
let manager: BleManager;
|
|
88
|
+
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
manager = new BleManager();
|
|
91
|
+
jest.clearAllMocks();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
afterEach(async () => {
|
|
95
|
+
await manager.destroy();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('Initialization', () => {
|
|
99
|
+
it('should create a BleManager instance', () => {
|
|
100
|
+
expect(manager).toBeInstanceOf(BleManager);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should set log level', async () => {
|
|
104
|
+
const result = await manager.setLogLevel(LogLevel.Info);
|
|
105
|
+
expect(result).toBe('Info'); // BleManagerCompat returns strings for compatibility
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should get current log level', async () => {
|
|
109
|
+
await manager.setLogLevel(LogLevel.Debug);
|
|
110
|
+
const level = await manager.logLevel();
|
|
111
|
+
expect(level).toBe('Info'); // BleManagerCompat returns strings for compatibility
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('State Management', () => {
|
|
116
|
+
it('should get current state', async () => {
|
|
117
|
+
const state = await manager.state();
|
|
118
|
+
expect(state).toBe('PoweredOn'); // BleManagerCompat converts State.PoweredOn -> 'PoweredOn'
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should listen to state changes', async () => {
|
|
122
|
+
const listener = jest.fn();
|
|
123
|
+
const subscription = manager.onStateChange(listener, true);
|
|
124
|
+
|
|
125
|
+
expect(subscription).toHaveProperty('remove');
|
|
126
|
+
expect(typeof subscription.remove).toBe('function');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('Device Scanning', () => {
|
|
131
|
+
it('should start device scan', async () => {
|
|
132
|
+
const listener = jest.fn();
|
|
133
|
+
await manager.startDeviceScan(null, null, listener);
|
|
134
|
+
|
|
135
|
+
// Verify that the native method was called
|
|
136
|
+
expect(mockNitroManager.startDeviceScan).toHaveBeenCalledWith(
|
|
137
|
+
null,
|
|
138
|
+
null,
|
|
139
|
+
expect.any(Function)
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should stop device scan', async () => {
|
|
144
|
+
await manager.stopDeviceScan();
|
|
145
|
+
|
|
146
|
+
expect(mockNitroManager.stopDeviceScan).toHaveBeenCalled();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should filter UUIDs when scanning', async () => {
|
|
150
|
+
const uuids = ['12345678-1234-5678-9abc-123456789abc'];
|
|
151
|
+
const listener = jest.fn();
|
|
152
|
+
|
|
153
|
+
await manager.startDeviceScan(uuids, null, listener);
|
|
154
|
+
|
|
155
|
+
expect(mockNitroManager.startDeviceScan).toHaveBeenCalledWith(
|
|
156
|
+
uuids,
|
|
157
|
+
null,
|
|
158
|
+
expect.any(Function)
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('Device Connection', () => {
|
|
164
|
+
const deviceId = 'test-device';
|
|
165
|
+
|
|
166
|
+
it('should connect to device', async () => {
|
|
167
|
+
const device = await manager.connectToDevice(deviceId);
|
|
168
|
+
|
|
169
|
+
expect(device.id).toBe(deviceId);
|
|
170
|
+
expect(device.name).toBe('Test Device');
|
|
171
|
+
expect(mockNitroManager.connectToDevice).toHaveBeenCalledWith(
|
|
172
|
+
deviceId,
|
|
173
|
+
expect.any(Object)
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should check if device is connected', async () => {
|
|
178
|
+
const isConnected = await manager.isDeviceConnected(deviceId);
|
|
179
|
+
expect(isConnected).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should cancel device connection', async () => {
|
|
183
|
+
const device = await manager.cancelDeviceConnection(deviceId);
|
|
184
|
+
expect(device.id).toBe(deviceId);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should discover services and characteristics', async () => {
|
|
188
|
+
const device = await manager.discoverAllServicesAndCharacteristicsForDevice(deviceId);
|
|
189
|
+
expect(device.id).toBe(deviceId);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('GATT Operations', () => {
|
|
194
|
+
const deviceId = 'test-device';
|
|
195
|
+
const serviceUUID = '12345678-1234-5678-9abc-123456789abc';
|
|
196
|
+
const characteristicUUID = '87654321-4321-8765-cba9-987654321cba';
|
|
197
|
+
|
|
198
|
+
it('should list services for device', async () => {
|
|
199
|
+
const services = await manager.servicesForDevice(deviceId);
|
|
200
|
+
expect(services).toHaveLength(1);
|
|
201
|
+
expect(services[0].uuid).toBe(serviceUUID);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should list characteristics for service', async () => {
|
|
205
|
+
const characteristics = await manager.characteristicsForDevice(deviceId, serviceUUID);
|
|
206
|
+
expect(characteristics).toHaveLength(1);
|
|
207
|
+
expect(characteristics[0].uuid).toBe(characteristicUUID);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should read characteristic value', async () => {
|
|
211
|
+
const characteristic = await manager.readCharacteristicForDevice(
|
|
212
|
+
deviceId,
|
|
213
|
+
serviceUUID,
|
|
214
|
+
characteristicUUID
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
expect(characteristic.value).toBe('dGVzdCB2YWx1ZQ==');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should write characteristic with response', async () => {
|
|
221
|
+
const value = 'dGVzdCB2YWx1ZQ=='; // "test value" in base64
|
|
222
|
+
|
|
223
|
+
const characteristic = await manager.writeCharacteristicWithResponseForDevice(
|
|
224
|
+
deviceId,
|
|
225
|
+
serviceUUID,
|
|
226
|
+
characteristicUUID,
|
|
227
|
+
value
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
expect(characteristic.value).toBe(value);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should monitor characteristic for changes', async () => {
|
|
234
|
+
const listener = jest.fn();
|
|
235
|
+
|
|
236
|
+
const subscription = manager.monitorCharacteristicForDevice(
|
|
237
|
+
deviceId,
|
|
238
|
+
serviceUUID,
|
|
239
|
+
characteristicUUID,
|
|
240
|
+
listener
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(subscription).toHaveProperty('remove');
|
|
244
|
+
expect(mockNitroManager.monitorCharacteristicForDevice).toHaveBeenCalledWith(
|
|
245
|
+
deviceId,
|
|
246
|
+
serviceUUID,
|
|
247
|
+
characteristicUUID,
|
|
248
|
+
expect.any(Function),
|
|
249
|
+
undefined,
|
|
250
|
+
undefined
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe('Error Handling', () => {
|
|
256
|
+
it('should handle connection errors gracefully', async () => {
|
|
257
|
+
// Mock a connection failure
|
|
258
|
+
const mockError = new Error('Connection failed');
|
|
259
|
+
(mockNitroManager.connectToDevice as jest.Mock).mockRejectedValueOnce(mockError);
|
|
260
|
+
|
|
261
|
+
await expect(manager.connectToDevice('invalid-device')).rejects.toThrow('Connection failed');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should handle scan start errors', async () => {
|
|
265
|
+
const mockError = new Error('Scan failed');
|
|
266
|
+
(mockNitroManager.startDeviceScan as jest.Mock).mockRejectedValueOnce(mockError);
|
|
267
|
+
|
|
268
|
+
const listener = jest.fn();
|
|
269
|
+
await expect(manager.startDeviceScan(null, null, listener)).rejects.toThrow('Scan failed');
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('Cleanup', () => {
|
|
274
|
+
it('should destroy manager and cleanup resources', async () => {
|
|
275
|
+
await manager.destroy();
|
|
276
|
+
expect(mockNitroManager.destroy).toHaveBeenCalled();
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
describe('Device Wrapper Compatibility', () => {
|
|
282
|
+
let manager: BleManager;
|
|
283
|
+
|
|
284
|
+
beforeEach(() => {
|
|
285
|
+
manager = new BleManager();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
afterEach(async () => {
|
|
289
|
+
await manager.destroy();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should provide react-native-ble-plx compatible API', async () => {
|
|
293
|
+
const deviceId = 'test-device';
|
|
294
|
+
|
|
295
|
+
// Connect to device
|
|
296
|
+
const device = await manager.connectToDevice(deviceId);
|
|
297
|
+
|
|
298
|
+
// The device should have all expected properties
|
|
299
|
+
expect(device).toHaveProperty('id');
|
|
300
|
+
expect(device).toHaveProperty('name');
|
|
301
|
+
expect(device).toHaveProperty('rssi');
|
|
302
|
+
expect(device).toHaveProperty('mtu');
|
|
303
|
+
|
|
304
|
+
// The device should have connect/disconnect methods
|
|
305
|
+
expect(device).toHaveProperty('connect');
|
|
306
|
+
expect(device).toHaveProperty('cancelConnection');
|
|
307
|
+
expect(device).toHaveProperty('isConnected');
|
|
308
|
+
expect(device).toHaveProperty('discoverAllServicesAndCharacteristics');
|
|
309
|
+
|
|
310
|
+
// All methods should be functions
|
|
311
|
+
expect(typeof device.connect).toBe('function');
|
|
312
|
+
expect(typeof device.cancelConnection).toBe('function');
|
|
313
|
+
expect(typeof device.isConnected).toBe('function');
|
|
314
|
+
expect(typeof device.discoverAllServicesAndCharacteristics).toBe('function');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should convert enum values to strings for compatibility', async () => {
|
|
318
|
+
const state = await manager.state();
|
|
319
|
+
|
|
320
|
+
// State should be a string value (mocked return value is 'PoweredOn')
|
|
321
|
+
expect(typeof state).toBe('string');
|
|
322
|
+
expect(state).toBe('PoweredOn');
|
|
323
|
+
|
|
324
|
+
// The compatibility layer should handle string/number conversion
|
|
325
|
+
expect(State.PoweredOn).toBe(5); // Numeric enum value
|
|
326
|
+
});
|
|
327
|
+
});
|