@onekeyfe/hd-transport-react-native 1.2.0-alpha.66 → 1.2.0-alpha.67
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/dist/BleTransport.d.ts.map +1 -1
- package/dist/index.d.ts +79 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +308 -109
- package/jest.config.js +5 -0
- package/package.json +5 -5
- package/src/BleTransport.ts +0 -6
- package/src/__tests__/BleTransport.test.ts +2 -54
- package/src/__tests__/connectTimeout.test.ts +281 -0
- package/src/__tests__/protocolReprobe.test.ts +132 -0
- package/src/__tests__/protocolV1SchemaFixture.ts +39 -0
- package/src/__tests__/protocolV2Link.test.ts +48 -281
- package/src/__tests__/staleCallTimeout.test.ts +210 -0
- package/src/__tests__/writePacketTimeout.test.ts +305 -0
- package/src/index.ts +451 -112
- package/src/__tests__/enumerate.test.ts +0 -132
|
@@ -3,6 +3,7 @@ import transportPackage, {
|
|
|
3
3
|
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
4
4
|
ProtocolV2,
|
|
5
5
|
TRANSPORT_EVENT,
|
|
6
|
+
bytesToHex,
|
|
6
7
|
} from '@onekeyfe/hd-transport';
|
|
7
8
|
import { HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
|
|
8
9
|
|
|
@@ -45,11 +46,6 @@ jest.mock('../subscribeBleOn', () => ({
|
|
|
45
46
|
subscribeBleOn: jest.fn(() => Promise.resolve()),
|
|
46
47
|
}));
|
|
47
48
|
|
|
48
|
-
const setPlatformOS = (os: 'ios' | 'android') => {
|
|
49
|
-
const reactNative: { Platform: { OS: string } } = jest.requireMock('react-native');
|
|
50
|
-
reactNative.Platform.OS = os;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
49
|
const { parseConfigure } = transportPackage;
|
|
54
50
|
|
|
55
51
|
const protocolV1Schema = {
|
|
@@ -73,13 +69,11 @@ const protocolV1Schema = {
|
|
|
73
69
|
|
|
74
70
|
const protocolV2Schema = {
|
|
75
71
|
nested: {
|
|
76
|
-
ProtocolInfoRequest: { fields: {} },
|
|
77
72
|
Ping: {
|
|
78
73
|
fields: {
|
|
79
74
|
message: { type: 'string', id: 1 },
|
|
80
75
|
},
|
|
81
76
|
},
|
|
82
|
-
DeviceInfoGet: { fields: {} },
|
|
83
77
|
FileWrite: { fields: {} },
|
|
84
78
|
Success: {
|
|
85
79
|
fields: {
|
|
@@ -88,10 +82,8 @@ const protocolV2Schema = {
|
|
|
88
82
|
},
|
|
89
83
|
MessageType: {
|
|
90
84
|
values: {
|
|
91
|
-
MessageType_ProtocolInfoRequest: 60200,
|
|
92
85
|
MessageType_Ping: 60206,
|
|
93
86
|
MessageType_Success: 60207,
|
|
94
|
-
MessageType_DeviceInfoGet: 60600,
|
|
95
87
|
MessageType_FileWrite: 60805,
|
|
96
88
|
},
|
|
97
89
|
},
|
|
@@ -103,13 +95,7 @@ const schemas = {
|
|
|
103
95
|
protocolV2: parseConfigure(protocolV2Schema),
|
|
104
96
|
};
|
|
105
97
|
|
|
106
|
-
const createHarness = ({
|
|
107
|
-
deviceName = 'OneKey Pro 2',
|
|
108
|
-
isWritableWithResponse = true,
|
|
109
|
-
}: {
|
|
110
|
-
deviceName?: string;
|
|
111
|
-
isWritableWithResponse?: boolean;
|
|
112
|
-
} = {}) => {
|
|
98
|
+
const createHarness = () => {
|
|
113
99
|
const uuid = 'rn-pro2-id';
|
|
114
100
|
const sentSeqs: number[] = [];
|
|
115
101
|
let responseSeq = 0;
|
|
@@ -148,15 +134,15 @@ const createHarness = ({
|
|
|
148
134
|
const writeCharacteristic = {
|
|
149
135
|
uuid: '0002',
|
|
150
136
|
deviceID: uuid,
|
|
151
|
-
isWritableWithResponse,
|
|
137
|
+
isWritableWithResponse: true,
|
|
152
138
|
isWritableWithoutResponse: true,
|
|
153
139
|
writeWithResponse: jest.fn(handleWrite),
|
|
154
140
|
writeWithoutResponse: jest.fn(handleWrite),
|
|
155
141
|
};
|
|
156
142
|
const device = {
|
|
157
143
|
id: uuid,
|
|
158
|
-
name:
|
|
159
|
-
localName:
|
|
144
|
+
name: 'OneKey Pro 2',
|
|
145
|
+
localName: 'OneKey Pro 2',
|
|
160
146
|
serviceUUIDs: ['00000001-0000-1000-8000-00805f9b34fb'],
|
|
161
147
|
isConnected: jest.fn(() => Promise.resolve(true)),
|
|
162
148
|
cancelConnection: jest.fn(() => Promise.resolve()),
|
|
@@ -198,13 +184,7 @@ const createHarness = ({
|
|
|
198
184
|
};
|
|
199
185
|
};
|
|
200
186
|
|
|
201
|
-
const createV1Harness = ({
|
|
202
|
-
respondOnWriteCount = 1,
|
|
203
|
-
isWritableWithResponse = true,
|
|
204
|
-
}: {
|
|
205
|
-
respondOnWriteCount?: number | number[];
|
|
206
|
-
isWritableWithResponse?: boolean;
|
|
207
|
-
} = {}) => {
|
|
187
|
+
const createV1Harness = () => {
|
|
208
188
|
const uuid = 'rn-classic-id';
|
|
209
189
|
const notifySubscriptionRemovers: jest.Mock[] = [];
|
|
210
190
|
const disconnectSubscriptionRemovers: jest.Mock[] = [];
|
|
@@ -223,25 +203,20 @@ const createV1Harness = ({
|
|
|
223
203
|
}),
|
|
224
204
|
};
|
|
225
205
|
let writeCount = 0;
|
|
226
|
-
const responseWriteCounts = new Set(
|
|
227
|
-
Array.isArray(respondOnWriteCount) ? respondOnWriteCount : [respondOnWriteCount]
|
|
228
|
-
);
|
|
229
|
-
const handleWrite = () => {
|
|
230
|
-
writeCount += 1;
|
|
231
|
-
if (responseWriteCounts.has(writeCount)) {
|
|
232
|
-
notifyCallback?.(null, {
|
|
233
|
-
value: Buffer.from('3f23230002000000040a026f6b', 'hex').toString('base64'),
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
return Promise.resolve();
|
|
237
|
-
};
|
|
238
206
|
const writeCharacteristic = {
|
|
239
207
|
uuid: '0002',
|
|
240
208
|
deviceID: uuid,
|
|
241
|
-
isWritableWithResponse,
|
|
209
|
+
isWritableWithResponse: true,
|
|
242
210
|
isWritableWithoutResponse: true,
|
|
243
|
-
|
|
244
|
-
|
|
211
|
+
writeWithoutResponse: jest.fn(() => {
|
|
212
|
+
writeCount += 1;
|
|
213
|
+
if (writeCount === 1) {
|
|
214
|
+
notifyCallback?.(null, {
|
|
215
|
+
value: Buffer.from('3f23230002000000040a026f6b', 'hex').toString('base64'),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return Promise.resolve();
|
|
219
|
+
}),
|
|
245
220
|
};
|
|
246
221
|
const device = {
|
|
247
222
|
id: uuid,
|
|
@@ -274,7 +249,6 @@ const createV1Harness = ({
|
|
|
274
249
|
uuid,
|
|
275
250
|
device,
|
|
276
251
|
bleManager,
|
|
277
|
-
writeCharacteristic,
|
|
278
252
|
notifySubscriptionRemovers,
|
|
279
253
|
disconnectSubscriptionRemovers,
|
|
280
254
|
};
|
|
@@ -290,19 +264,6 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
290
264
|
).toBeNull();
|
|
291
265
|
});
|
|
292
266
|
|
|
293
|
-
test.each(['status 143', 'status:143', 'status = 143', 'GATT_CONGESTED'])(
|
|
294
|
-
'classifies %s as transient GATT congestion',
|
|
295
|
-
message => {
|
|
296
|
-
expect(getFirmwareUploadWriteRetryType({ message })).toBe('congested');
|
|
297
|
-
}
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
test('handles long uncontrolled status messages without a backtracking regular expression', () => {
|
|
301
|
-
const message = `status${' '.repeat(100_000)}142`;
|
|
302
|
-
|
|
303
|
-
expect(getFirmwareUploadWriteRetryType({ message })).toBeNull();
|
|
304
|
-
});
|
|
305
|
-
|
|
306
267
|
test('keeps another device reader when releasing a device with an active V1 call', async () => {
|
|
307
268
|
const transport = new ReactNativeBleTransport({ scanTimeout: 1 }) as any;
|
|
308
269
|
const activeV1Call = createDeferred<string>();
|
|
@@ -341,101 +302,12 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
341
302
|
expect(new ReactNativeBleTransport({}).scanTimeout).toBe(3000);
|
|
342
303
|
});
|
|
343
304
|
|
|
344
|
-
test('uses withResponse for consecutive iOS Protocol V1 control commands without releasing', async () => {
|
|
345
|
-
const { transport, uuid, writeCharacteristic } = createV1Harness({
|
|
346
|
-
respondOnWriteCount: [1, 2],
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
await expect(transport.acquire({ uuid, expectedProtocol: 'V1' })).resolves.toEqual({
|
|
350
|
-
uuid,
|
|
351
|
-
protocolType: 'V1',
|
|
352
|
-
});
|
|
353
|
-
const releaseNative = jest.spyOn(transport as any, 'releaseNative');
|
|
354
|
-
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
355
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
356
|
-
|
|
357
|
-
await expect(transport.call(uuid, 'Initialize', {}, { timeoutMs: 50 })).resolves.toBeDefined();
|
|
358
|
-
await expect(transport.call(uuid, 'GetFeatures', {}, { timeoutMs: 50 })).resolves.toBeDefined();
|
|
359
|
-
|
|
360
|
-
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(2);
|
|
361
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
362
|
-
expect(releaseNative).not.toHaveBeenCalled();
|
|
363
|
-
await transport.release(uuid, true);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
test('falls back to withoutResponse for an iOS Protocol V1 control command when required', async () => {
|
|
367
|
-
const { transport, uuid, writeCharacteristic } = createV1Harness({
|
|
368
|
-
isWritableWithResponse: false,
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
await transport.acquire({ uuid, expectedProtocol: 'V1' });
|
|
372
|
-
await expect(transport.call(uuid, 'Initialize', {}, { timeoutMs: 50 })).resolves.toBeDefined();
|
|
373
|
-
|
|
374
|
-
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
375
|
-
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
376
|
-
await transport.release(uuid, true);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test('does not resend a failed iOS Protocol V1 control write without response', async () => {
|
|
380
|
-
const { transport, uuid, writeCharacteristic } = createV1Harness();
|
|
381
|
-
const writeError = new Error('write with response failed');
|
|
382
|
-
|
|
383
|
-
await transport.acquire({ uuid, expectedProtocol: 'V1' });
|
|
384
|
-
writeCharacteristic.writeWithResponse.mockRejectedValueOnce(writeError);
|
|
385
|
-
|
|
386
|
-
await expect(transport.call(uuid, 'Initialize', {}, { timeoutMs: 50 })).rejects.toMatchObject({
|
|
387
|
-
errorCode: HardwareErrorCode.BleWriteCharacteristicError,
|
|
388
|
-
});
|
|
389
|
-
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(1);
|
|
390
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
391
|
-
await transport.release(uuid, true);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
test('actively probes Protocol V2 on iOS when only a name-derived hint is available', async () => {
|
|
395
|
-
const { transport, uuid, sentSeqs, writeCharacteristic } = createHarness({
|
|
396
|
-
deviceName: 'Pro2 6E9E',
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
await expect(transport.acquire({ uuid })).resolves.toEqual({
|
|
400
|
-
uuid,
|
|
401
|
-
protocolType: 'V2',
|
|
402
|
-
});
|
|
403
|
-
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(1);
|
|
404
|
-
|
|
405
|
-
await expect(
|
|
406
|
-
transport.call(uuid, 'Ping', { message: 'first-core-command' })
|
|
407
|
-
).resolves.toBeDefined();
|
|
408
|
-
expect(sentSeqs).toEqual([1, 2]);
|
|
409
|
-
await transport.release(uuid, true);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
test('falls back to the other active probe on iOS when protocol metadata is absent', async () => {
|
|
413
|
-
const { transport, uuid } = createHarness({ deviceName: 'OneKey' });
|
|
414
|
-
const probeProtocolV1 = jest
|
|
415
|
-
.spyOn(transport as any, 'probeProtocolV1')
|
|
416
|
-
.mockResolvedValue(false);
|
|
417
|
-
const probeProtocolV2 = jest.spyOn(transport as any, 'probeProtocolV2').mockResolvedValue(true);
|
|
418
|
-
|
|
419
|
-
await expect(transport.acquire({ uuid })).resolves.toEqual({
|
|
420
|
-
uuid,
|
|
421
|
-
protocolType: 'V2',
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
expect(probeProtocolV1).toHaveBeenCalledTimes(1);
|
|
425
|
-
expect(probeProtocolV2).toHaveBeenCalledTimes(1);
|
|
426
|
-
expect(probeProtocolV1.mock.invocationCallOrder[0]).toBeLessThan(
|
|
427
|
-
probeProtocolV2.mock.invocationCallOrder[0]
|
|
428
|
-
);
|
|
429
|
-
await transport.release(uuid, true);
|
|
430
|
-
});
|
|
431
|
-
|
|
432
305
|
test('reconnects before falling back to Protocol V1 after a fatal V2 probe failure', async () => {
|
|
433
|
-
setPlatformOS('android');
|
|
434
306
|
const { transport, uuid, device, notifySubscriptionRemovers, disconnectSubscriptionRemovers } =
|
|
435
307
|
createV1Harness();
|
|
436
308
|
const probeProtocolV2 = jest
|
|
437
309
|
.spyOn(transport as any, 'probeProtocolV2')
|
|
438
|
-
.
|
|
310
|
+
.mockImplementationOnce(async () => {
|
|
439
311
|
await (transport as any).releaseNative(uuid, true);
|
|
440
312
|
return false;
|
|
441
313
|
});
|
|
@@ -461,9 +333,8 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
461
333
|
});
|
|
462
334
|
|
|
463
335
|
test('cleans the rebuilt transport when Protocol V1 fallback also fails', async () => {
|
|
464
|
-
setPlatformOS('android');
|
|
465
336
|
const { transport, uuid, device, bleManager, notifySubscriptionRemovers } = createV1Harness();
|
|
466
|
-
jest.spyOn(transport as any, 'probeProtocolV2').
|
|
337
|
+
jest.spyOn(transport as any, 'probeProtocolV2').mockImplementationOnce(async () => {
|
|
467
338
|
await (transport as any).releaseNative(uuid, true);
|
|
468
339
|
return false;
|
|
469
340
|
});
|
|
@@ -482,9 +353,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
482
353
|
});
|
|
483
354
|
|
|
484
355
|
test('disconnects and invalidates a Protocol V1 link after a response timeout', async () => {
|
|
485
|
-
const { transport, uuid, device } = createV1Harness(
|
|
486
|
-
respondOnWriteCount: Number.POSITIVE_INFINITY,
|
|
487
|
-
});
|
|
356
|
+
const { transport, uuid, device } = createV1Harness();
|
|
488
357
|
|
|
489
358
|
await transport.acquire({ uuid, expectedProtocol: 'V1' });
|
|
490
359
|
await expect(transport.call(uuid, 'Initialize', {}, { timeoutMs: 5 })).rejects.toMatchObject({
|
|
@@ -496,31 +365,29 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
496
365
|
});
|
|
497
366
|
|
|
498
367
|
afterEach(() => {
|
|
499
|
-
setPlatformOS('ios');
|
|
500
368
|
resetProtocolV2BleTuning();
|
|
501
369
|
});
|
|
502
370
|
|
|
503
|
-
test('
|
|
371
|
+
test('keeps the Protocol V2 sequence across probe and the next call', async () => {
|
|
504
372
|
const { transport, uuid, sentSeqs } = createHarness();
|
|
505
373
|
|
|
506
|
-
await transport.acquire({ uuid
|
|
507
|
-
await transport.call(uuid, 'Ping', { message: '
|
|
374
|
+
await transport.acquire({ uuid });
|
|
375
|
+
await transport.call(uuid, 'Ping', { message: 'after-probe' });
|
|
508
376
|
|
|
509
|
-
expect(sentSeqs).toEqual([1]);
|
|
377
|
+
expect(sentSeqs).toEqual([1, 2]);
|
|
378
|
+
expect(bytesToHex(new Uint8Array([sentSeqs[0], sentSeqs[1]]))).toBe('0102');
|
|
510
379
|
await transport.release(uuid, true);
|
|
511
380
|
});
|
|
512
381
|
|
|
513
382
|
test('rejects the active Protocol V2 reader when the current monitor errors', async () => {
|
|
514
383
|
const harness = createHarness();
|
|
515
384
|
const { transport, uuid, sentSeqs } = harness;
|
|
516
|
-
await transport.acquire({ uuid
|
|
385
|
+
await transport.acquire({ uuid });
|
|
517
386
|
harness.setShouldRespond(false);
|
|
518
387
|
|
|
519
388
|
const call = transport.call(uuid, 'Ping', { message: 'wait-for-monitor' }, { timeoutMs: 50 });
|
|
520
|
-
while (sentSeqs.length <
|
|
521
|
-
await
|
|
522
|
-
setTimeout(resolve, 0);
|
|
523
|
-
});
|
|
389
|
+
while (sentSeqs.length < 2) {
|
|
390
|
+
await Promise.resolve();
|
|
524
391
|
}
|
|
525
392
|
await new Promise(resolve => {
|
|
526
393
|
setTimeout(resolve, 0);
|
|
@@ -535,147 +402,40 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
535
402
|
test('retains the sequence cursor when a new monitor generation is acquired', async () => {
|
|
536
403
|
const { transport, uuid, sentSeqs } = createHarness();
|
|
537
404
|
|
|
538
|
-
await transport.acquire({ uuid
|
|
539
|
-
await transport.call(uuid, 'Ping', { message: 'first-generation' });
|
|
405
|
+
await transport.acquire({ uuid });
|
|
540
406
|
await transport.release(uuid, true);
|
|
541
|
-
await transport.acquire({ uuid
|
|
542
|
-
await transport.call(uuid, 'Ping', { message: 'second-generation' });
|
|
407
|
+
await transport.acquire({ uuid });
|
|
543
408
|
|
|
544
409
|
expect(sentSeqs).toEqual([1, 2]);
|
|
545
410
|
await transport.release(uuid, true);
|
|
546
411
|
});
|
|
547
412
|
|
|
548
|
-
test('uses
|
|
413
|
+
test('uses withoutResponse for normal and high-volume calls', async () => {
|
|
549
414
|
const { transport, uuid, writeCharacteristic } = createHarness();
|
|
550
415
|
|
|
551
|
-
await transport.acquire({ uuid
|
|
552
|
-
const releaseNative = jest.spyOn(transport as any, 'releaseNative');
|
|
553
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
554
|
-
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
555
|
-
|
|
556
|
-
await transport.call(uuid, 'DeviceInfoGet', {});
|
|
557
|
-
await transport.call(uuid, 'ProtocolInfoRequest', {});
|
|
558
|
-
|
|
559
|
-
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(2);
|
|
560
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
561
|
-
expect(releaseNative).not.toHaveBeenCalled();
|
|
562
|
-
|
|
563
|
-
await transport.release(uuid, true);
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
test('keeps iOS Protocol V2 high-volume calls on withoutResponse', async () => {
|
|
567
|
-
const { transport, uuid, writeCharacteristic } = createHarness();
|
|
568
|
-
|
|
569
|
-
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
570
|
-
|
|
571
|
-
await transport.call(uuid, 'FileWrite', {});
|
|
416
|
+
await transport.acquire({ uuid });
|
|
572
417
|
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
573
418
|
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
574
|
-
await transport.release(uuid, true);
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
test('uses withResponse for an iOS Protocol V2 firmware file write when requested', async () => {
|
|
578
|
-
const { transport, uuid, writeCharacteristic } = createHarness();
|
|
579
|
-
|
|
580
|
-
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
581
|
-
|
|
582
|
-
await transport.call(uuid, 'FileWrite', {}, { writeWithResponse: true });
|
|
583
|
-
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(1);
|
|
584
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
585
|
-
await transport.release(uuid, true);
|
|
586
|
-
});
|
|
587
419
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
594
|
-
await transport.call(uuid, 'ProtocolInfoRequest', {});
|
|
420
|
+
await transport.call(uuid, 'Ping', { message: 'normal' });
|
|
421
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(2);
|
|
422
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
595
423
|
|
|
424
|
+
await transport.call(uuid, 'FileWrite', {});
|
|
425
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(3);
|
|
596
426
|
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
597
|
-
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
598
427
|
await transport.release(uuid, true);
|
|
599
428
|
});
|
|
600
429
|
|
|
601
|
-
test('does not resend a failed iOS Protocol V2 control write without response', async () => {
|
|
602
|
-
const transport = new ReactNativeBleTransport({ scanTimeout: 1 }) as any;
|
|
603
|
-
const writeError = new Error('write with response failed');
|
|
604
|
-
const writeWithResponse = jest.fn().mockRejectedValue(writeError);
|
|
605
|
-
const writeWithoutResponse = jest.fn().mockResolvedValue(undefined);
|
|
606
|
-
const context = {
|
|
607
|
-
messageName: 'ProtocolInfoRequest',
|
|
608
|
-
timeoutMs: 1000,
|
|
609
|
-
highVolume: false,
|
|
610
|
-
generation: 1,
|
|
611
|
-
signal: new AbortController().signal,
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
await expect(
|
|
615
|
-
transport.writeProtocolV2Packet(
|
|
616
|
-
{
|
|
617
|
-
writeCharacteristic: {
|
|
618
|
-
isWritableWithResponse: true,
|
|
619
|
-
writeWithResponse,
|
|
620
|
-
writeWithoutResponse,
|
|
621
|
-
},
|
|
622
|
-
},
|
|
623
|
-
Buffer.from('control').toString('base64'),
|
|
624
|
-
context,
|
|
625
|
-
jest.fn()
|
|
626
|
-
)
|
|
627
|
-
).rejects.toBe(writeError);
|
|
628
|
-
expect(writeWithResponse).toHaveBeenCalledTimes(1);
|
|
629
|
-
expect(writeWithoutResponse).not.toHaveBeenCalled();
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
test('paces a one-packet Protocol V2 control write on iOS', async () => {
|
|
633
|
-
const transport = new ReactNativeBleTransport({ scanTimeout: 1 }) as any;
|
|
634
|
-
const writeWithoutResponse = jest.fn().mockResolvedValue(undefined);
|
|
635
|
-
const bleTransport = {
|
|
636
|
-
mtuSize: 23,
|
|
637
|
-
writeCharacteristic: { writeWithoutResponse },
|
|
638
|
-
};
|
|
639
|
-
const context = {
|
|
640
|
-
messageName: 'ProtocolInfoRequest',
|
|
641
|
-
timeoutMs: 1000,
|
|
642
|
-
highVolume: false,
|
|
643
|
-
generation: 1,
|
|
644
|
-
signal: new AbortController().signal,
|
|
645
|
-
};
|
|
646
|
-
configureProtocolV2BleTuning({ iosPacketLength: 20 });
|
|
647
|
-
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
648
|
-
|
|
649
|
-
try {
|
|
650
|
-
const call = transport.writeProtocolV2Frame(
|
|
651
|
-
bleTransport,
|
|
652
|
-
new Uint8Array(10),
|
|
653
|
-
context,
|
|
654
|
-
jest.fn()
|
|
655
|
-
);
|
|
656
|
-
|
|
657
|
-
await Promise.resolve();
|
|
658
|
-
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 5);
|
|
659
|
-
expect(writeWithoutResponse).not.toHaveBeenCalled();
|
|
660
|
-
|
|
661
|
-
await call;
|
|
662
|
-
expect(writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
663
|
-
} finally {
|
|
664
|
-
setTimeoutSpy.mockRestore();
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
|
|
668
430
|
test('rejects an active Protocol V2 reader when disconnect resets the link', async () => {
|
|
669
431
|
const harness = createHarness();
|
|
670
432
|
const { transport, uuid, sentSeqs } = harness;
|
|
671
|
-
await transport.acquire({ uuid
|
|
433
|
+
await transport.acquire({ uuid });
|
|
672
434
|
harness.setShouldRespond(false);
|
|
673
435
|
|
|
674
436
|
const call = transport.call(uuid, 'Ping', { message: 'disconnect' }, { timeoutMs: 50 });
|
|
675
|
-
while (sentSeqs.length <
|
|
676
|
-
await
|
|
677
|
-
setTimeout(resolve, 0);
|
|
678
|
-
});
|
|
437
|
+
while (sentSeqs.length < 2) {
|
|
438
|
+
await Promise.resolve();
|
|
679
439
|
}
|
|
680
440
|
|
|
681
441
|
const rejection = expect(call).rejects.toThrow('React Native BLE transport disconnected');
|
|
@@ -688,7 +448,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
688
448
|
const disconnectListener = jest.fn();
|
|
689
449
|
harness.emitter.on(TRANSPORT_EVENT.DEVICE_DISCONNECT, disconnectListener);
|
|
690
450
|
|
|
691
|
-
await harness.transport.acquire({ uuid: harness.uuid
|
|
451
|
+
await harness.transport.acquire({ uuid: harness.uuid });
|
|
692
452
|
harness.emitDisconnect();
|
|
693
453
|
await harness.transport.disconnect(harness.uuid);
|
|
694
454
|
|
|
@@ -722,6 +482,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
722
482
|
configureProtocolV2BleTuning({ iosPacketLength: 20 });
|
|
723
483
|
|
|
724
484
|
await transport.writeProtocolV2Frame(
|
|
485
|
+
'device-uuid',
|
|
725
486
|
bleTransport,
|
|
726
487
|
new Uint8Array(30),
|
|
727
488
|
context,
|
|
@@ -753,7 +514,13 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
753
514
|
configureProtocolV2BleTuning({ iosPacketLength: 20 });
|
|
754
515
|
|
|
755
516
|
await expect(
|
|
756
|
-
transport.writeProtocolV2Frame(
|
|
517
|
+
transport.writeProtocolV2Frame(
|
|
518
|
+
'device-uuid',
|
|
519
|
+
bleTransport,
|
|
520
|
+
new Uint8Array(30),
|
|
521
|
+
context,
|
|
522
|
+
jest.fn()
|
|
523
|
+
)
|
|
757
524
|
).rejects.toMatchObject({ errorCode: 205 });
|
|
758
525
|
expect(writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
759
526
|
});
|