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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +298 -0
  3. package/android/build.gradle +55 -0
  4. package/android/src/main/AndroidManifest.xml +23 -0
  5. package/android/src/main/kotlin/co/zyke/ble/BleNitroBleManager.kt +651 -0
  6. package/android/src/main/kotlin/co/zyke/ble/BleNitroPackage.kt +37 -0
  7. package/ios/BleNitro.podspec +37 -0
  8. package/ios/BleNitroBleManager.swift +509 -0
  9. package/ios/BleNitroModule.swift +31 -0
  10. package/lib/BleManagerCompatFactory.d.ts +53 -0
  11. package/lib/BleManagerCompatFactory.js +191 -0
  12. package/lib/BleManagerFactory.d.ts +12 -0
  13. package/lib/BleManagerFactory.js +22 -0
  14. package/lib/compatibility/constants.d.ts +49 -0
  15. package/lib/compatibility/constants.js +50 -0
  16. package/lib/compatibility/deviceWrapper.d.ts +99 -0
  17. package/lib/compatibility/deviceWrapper.js +259 -0
  18. package/lib/compatibility/enums.d.ts +43 -0
  19. package/lib/compatibility/enums.js +124 -0
  20. package/lib/compatibility/index.d.ts +11 -0
  21. package/lib/compatibility/index.js +12 -0
  22. package/lib/compatibility/serviceData.d.ts +51 -0
  23. package/lib/compatibility/serviceData.js +70 -0
  24. package/lib/errors/BleError.d.ts +59 -0
  25. package/lib/errors/BleError.js +120 -0
  26. package/lib/index.d.ts +7 -0
  27. package/lib/index.js +12 -0
  28. package/lib/specs/BleManager.nitro.d.ts +36 -0
  29. package/lib/specs/BleManager.nitro.js +1 -0
  30. package/lib/specs/Characteristic.nitro.d.ts +26 -0
  31. package/lib/specs/Characteristic.nitro.js +1 -0
  32. package/lib/specs/Descriptor.nitro.d.ts +17 -0
  33. package/lib/specs/Descriptor.nitro.js +1 -0
  34. package/lib/specs/Device.nitro.d.ts +37 -0
  35. package/lib/specs/Device.nitro.js +1 -0
  36. package/lib/specs/Service.nitro.d.ts +19 -0
  37. package/lib/specs/Service.nitro.js +1 -0
  38. package/lib/specs/types.d.ts +228 -0
  39. package/lib/specs/types.js +146 -0
  40. package/lib/utils/base64.d.ts +25 -0
  41. package/lib/utils/base64.js +80 -0
  42. package/lib/utils/index.d.ts +2 -0
  43. package/lib/utils/index.js +2 -0
  44. package/lib/utils/uuid.d.ts +9 -0
  45. package/lib/utils/uuid.js +37 -0
  46. package/nitro.json +15 -0
  47. package/package.json +102 -0
  48. package/plugin/build/index.d.ts +28 -0
  49. package/plugin/build/index.js +29 -0
  50. package/plugin/build/withBleNitro.d.ts +31 -0
  51. package/plugin/build/withBleNitro.js +87 -0
  52. package/react-native.config.js +13 -0
  53. package/src/BleManagerCompatFactory.ts +373 -0
  54. package/src/BleManagerFactory.ts +30 -0
  55. package/src/__tests__/BleManager.test.ts +327 -0
  56. package/src/__tests__/compatibility/deviceWrapper.test.ts +563 -0
  57. package/src/__tests__/compatibility/enums.test.ts +254 -0
  58. package/src/compatibility/constants.ts +71 -0
  59. package/src/compatibility/deviceWrapper.ts +427 -0
  60. package/src/compatibility/enums.ts +160 -0
  61. package/src/compatibility/index.ts +24 -0
  62. package/src/compatibility/serviceData.ts +85 -0
  63. package/src/errors/BleError.ts +193 -0
  64. package/src/index.ts +30 -0
  65. package/src/specs/BleManager.nitro.ts +152 -0
  66. package/src/specs/Characteristic.nitro.ts +61 -0
  67. package/src/specs/Descriptor.nitro.ts +28 -0
  68. package/src/specs/Device.nitro.ts +104 -0
  69. package/src/specs/Service.nitro.ts +64 -0
  70. package/src/specs/types.ts +259 -0
  71. package/src/utils/base64.ts +80 -0
  72. package/src/utils/index.ts +2 -0
  73. 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
+ });