@onekeyfe/hd-transport-react-native 1.2.0-alpha.65 → 1.2.0-alpha.66

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.
@@ -3,7 +3,6 @@ import transportPackage, {
3
3
  PROTOCOL_V2_CHANNEL_BLE_UART,
4
4
  ProtocolV2,
5
5
  TRANSPORT_EVENT,
6
- bytesToHex,
7
6
  } from '@onekeyfe/hd-transport';
8
7
  import { HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
9
8
 
@@ -46,6 +45,11 @@ jest.mock('../subscribeBleOn', () => ({
46
45
  subscribeBleOn: jest.fn(() => Promise.resolve()),
47
46
  }));
48
47
 
48
+ const setPlatformOS = (os: 'ios' | 'android') => {
49
+ const reactNative: { Platform: { OS: string } } = jest.requireMock('react-native');
50
+ reactNative.Platform.OS = os;
51
+ };
52
+
49
53
  const { parseConfigure } = transportPackage;
50
54
 
51
55
  const protocolV1Schema = {
@@ -69,11 +73,13 @@ const protocolV1Schema = {
69
73
 
70
74
  const protocolV2Schema = {
71
75
  nested: {
76
+ ProtocolInfoRequest: { fields: {} },
72
77
  Ping: {
73
78
  fields: {
74
79
  message: { type: 'string', id: 1 },
75
80
  },
76
81
  },
82
+ DeviceInfoGet: { fields: {} },
77
83
  FileWrite: { fields: {} },
78
84
  Success: {
79
85
  fields: {
@@ -82,8 +88,10 @@ const protocolV2Schema = {
82
88
  },
83
89
  MessageType: {
84
90
  values: {
91
+ MessageType_ProtocolInfoRequest: 60200,
85
92
  MessageType_Ping: 60206,
86
93
  MessageType_Success: 60207,
94
+ MessageType_DeviceInfoGet: 60600,
87
95
  MessageType_FileWrite: 60805,
88
96
  },
89
97
  },
@@ -95,7 +103,13 @@ const schemas = {
95
103
  protocolV2: parseConfigure(protocolV2Schema),
96
104
  };
97
105
 
98
- const createHarness = () => {
106
+ const createHarness = ({
107
+ deviceName = 'OneKey Pro 2',
108
+ isWritableWithResponse = true,
109
+ }: {
110
+ deviceName?: string;
111
+ isWritableWithResponse?: boolean;
112
+ } = {}) => {
99
113
  const uuid = 'rn-pro2-id';
100
114
  const sentSeqs: number[] = [];
101
115
  let responseSeq = 0;
@@ -134,15 +148,15 @@ const createHarness = () => {
134
148
  const writeCharacteristic = {
135
149
  uuid: '0002',
136
150
  deviceID: uuid,
137
- isWritableWithResponse: true,
151
+ isWritableWithResponse,
138
152
  isWritableWithoutResponse: true,
139
153
  writeWithResponse: jest.fn(handleWrite),
140
154
  writeWithoutResponse: jest.fn(handleWrite),
141
155
  };
142
156
  const device = {
143
157
  id: uuid,
144
- name: 'OneKey Pro 2',
145
- localName: 'OneKey Pro 2',
158
+ name: deviceName,
159
+ localName: deviceName,
146
160
  serviceUUIDs: ['00000001-0000-1000-8000-00805f9b34fb'],
147
161
  isConnected: jest.fn(() => Promise.resolve(true)),
148
162
  cancelConnection: jest.fn(() => Promise.resolve()),
@@ -184,7 +198,13 @@ const createHarness = () => {
184
198
  };
185
199
  };
186
200
 
187
- const createV1Harness = () => {
201
+ const createV1Harness = ({
202
+ respondOnWriteCount = 1,
203
+ isWritableWithResponse = true,
204
+ }: {
205
+ respondOnWriteCount?: number | number[];
206
+ isWritableWithResponse?: boolean;
207
+ } = {}) => {
188
208
  const uuid = 'rn-classic-id';
189
209
  const notifySubscriptionRemovers: jest.Mock[] = [];
190
210
  const disconnectSubscriptionRemovers: jest.Mock[] = [];
@@ -203,20 +223,25 @@ const createV1Harness = () => {
203
223
  }),
204
224
  };
205
225
  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
+ };
206
238
  const writeCharacteristic = {
207
239
  uuid: '0002',
208
240
  deviceID: uuid,
209
- isWritableWithResponse: true,
241
+ isWritableWithResponse,
210
242
  isWritableWithoutResponse: true,
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
- }),
243
+ writeWithResponse: jest.fn(handleWrite),
244
+ writeWithoutResponse: jest.fn(handleWrite),
220
245
  };
221
246
  const device = {
222
247
  id: uuid,
@@ -249,6 +274,7 @@ const createV1Harness = () => {
249
274
  uuid,
250
275
  device,
251
276
  bleManager,
277
+ writeCharacteristic,
252
278
  notifySubscriptionRemovers,
253
279
  disconnectSubscriptionRemovers,
254
280
  };
@@ -264,6 +290,19 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
264
290
  ).toBeNull();
265
291
  });
266
292
 
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
+
267
306
  test('keeps another device reader when releasing a device with an active V1 call', async () => {
268
307
  const transport = new ReactNativeBleTransport({ scanTimeout: 1 }) as any;
269
308
  const activeV1Call = createDeferred<string>();
@@ -302,12 +341,101 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
302
341
  expect(new ReactNativeBleTransport({}).scanTimeout).toBe(3000);
303
342
  });
304
343
 
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
+
305
432
  test('reconnects before falling back to Protocol V1 after a fatal V2 probe failure', async () => {
433
+ setPlatformOS('android');
306
434
  const { transport, uuid, device, notifySubscriptionRemovers, disconnectSubscriptionRemovers } =
307
435
  createV1Harness();
308
436
  const probeProtocolV2 = jest
309
437
  .spyOn(transport as any, 'probeProtocolV2')
310
- .mockImplementationOnce(async () => {
438
+ .mockImplementation(async () => {
311
439
  await (transport as any).releaseNative(uuid, true);
312
440
  return false;
313
441
  });
@@ -333,8 +461,9 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
333
461
  });
334
462
 
335
463
  test('cleans the rebuilt transport when Protocol V1 fallback also fails', async () => {
464
+ setPlatformOS('android');
336
465
  const { transport, uuid, device, bleManager, notifySubscriptionRemovers } = createV1Harness();
337
- jest.spyOn(transport as any, 'probeProtocolV2').mockImplementationOnce(async () => {
466
+ jest.spyOn(transport as any, 'probeProtocolV2').mockImplementation(async () => {
338
467
  await (transport as any).releaseNative(uuid, true);
339
468
  return false;
340
469
  });
@@ -353,7 +482,9 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
353
482
  });
354
483
 
355
484
  test('disconnects and invalidates a Protocol V1 link after a response timeout', async () => {
356
- const { transport, uuid, device } = createV1Harness();
485
+ const { transport, uuid, device } = createV1Harness({
486
+ respondOnWriteCount: Number.POSITIVE_INFINITY,
487
+ });
357
488
 
358
489
  await transport.acquire({ uuid, expectedProtocol: 'V1' });
359
490
  await expect(transport.call(uuid, 'Initialize', {}, { timeoutMs: 5 })).rejects.toMatchObject({
@@ -365,29 +496,31 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
365
496
  });
366
497
 
367
498
  afterEach(() => {
499
+ setPlatformOS('ios');
368
500
  resetProtocolV2BleTuning();
369
501
  });
370
502
 
371
- test('keeps the Protocol V2 sequence across probe and the next call', async () => {
503
+ test('uses the first Protocol V2 sequence for the first Core call when protocol is known', async () => {
372
504
  const { transport, uuid, sentSeqs } = createHarness();
373
505
 
374
- await transport.acquire({ uuid });
375
- await transport.call(uuid, 'Ping', { message: 'after-probe' });
506
+ await transport.acquire({ uuid, expectedProtocol: 'V2' });
507
+ await transport.call(uuid, 'Ping', { message: 'first-core-command' });
376
508
 
377
- expect(sentSeqs).toEqual([1, 2]);
378
- expect(bytesToHex(new Uint8Array([sentSeqs[0], sentSeqs[1]]))).toBe('0102');
509
+ expect(sentSeqs).toEqual([1]);
379
510
  await transport.release(uuid, true);
380
511
  });
381
512
 
382
513
  test('rejects the active Protocol V2 reader when the current monitor errors', async () => {
383
514
  const harness = createHarness();
384
515
  const { transport, uuid, sentSeqs } = harness;
385
- await transport.acquire({ uuid });
516
+ await transport.acquire({ uuid, expectedProtocol: 'V2' });
386
517
  harness.setShouldRespond(false);
387
518
 
388
519
  const call = transport.call(uuid, 'Ping', { message: 'wait-for-monitor' }, { timeoutMs: 50 });
389
- while (sentSeqs.length < 2) {
390
- await Promise.resolve();
520
+ while (sentSeqs.length < 1) {
521
+ await new Promise(resolve => {
522
+ setTimeout(resolve, 0);
523
+ });
391
524
  }
392
525
  await new Promise(resolve => {
393
526
  setTimeout(resolve, 0);
@@ -402,40 +535,147 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
402
535
  test('retains the sequence cursor when a new monitor generation is acquired', async () => {
403
536
  const { transport, uuid, sentSeqs } = createHarness();
404
537
 
405
- await transport.acquire({ uuid });
538
+ await transport.acquire({ uuid, expectedProtocol: 'V2' });
539
+ await transport.call(uuid, 'Ping', { message: 'first-generation' });
406
540
  await transport.release(uuid, true);
407
- await transport.acquire({ uuid });
541
+ await transport.acquire({ uuid, expectedProtocol: 'V2' });
542
+ await transport.call(uuid, 'Ping', { message: 'second-generation' });
408
543
 
409
544
  expect(sentSeqs).toEqual([1, 2]);
410
545
  await transport.release(uuid, true);
411
546
  });
412
547
 
413
- test('uses withoutResponse for normal and high-volume calls', async () => {
548
+ test('uses withResponse for consecutive iOS Protocol V2 control calls without releasing', async () => {
414
549
  const { transport, uuid, writeCharacteristic } = createHarness();
415
550
 
416
- await transport.acquire({ uuid });
417
- expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
551
+ await transport.acquire({ uuid, expectedProtocol: 'V2' });
552
+ const releaseNative = jest.spyOn(transport as any, 'releaseNative');
553
+ expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
418
554
  expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
419
555
 
420
- await transport.call(uuid, 'Ping', { message: 'normal' });
421
- expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(2);
422
- expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
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' });
423
570
 
424
571
  await transport.call(uuid, 'FileWrite', {});
425
- expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(3);
572
+ expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
573
+ 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
+
588
+ test('falls back to withoutResponse for an iOS Protocol V2 control call when required', async () => {
589
+ const { transport, uuid, writeCharacteristic } = createHarness({
590
+ isWritableWithResponse: false,
591
+ });
592
+
593
+ await transport.acquire({ uuid, expectedProtocol: 'V2' });
594
+ await transport.call(uuid, 'ProtocolInfoRequest', {});
595
+
426
596
  expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
597
+ expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
427
598
  await transport.release(uuid, true);
428
599
  });
429
600
 
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
+
430
668
  test('rejects an active Protocol V2 reader when disconnect resets the link', async () => {
431
669
  const harness = createHarness();
432
670
  const { transport, uuid, sentSeqs } = harness;
433
- await transport.acquire({ uuid });
671
+ await transport.acquire({ uuid, expectedProtocol: 'V2' });
434
672
  harness.setShouldRespond(false);
435
673
 
436
674
  const call = transport.call(uuid, 'Ping', { message: 'disconnect' }, { timeoutMs: 50 });
437
- while (sentSeqs.length < 2) {
438
- await Promise.resolve();
675
+ while (sentSeqs.length < 1) {
676
+ await new Promise(resolve => {
677
+ setTimeout(resolve, 0);
678
+ });
439
679
  }
440
680
 
441
681
  const rejection = expect(call).rejects.toThrow('React Native BLE transport disconnected');
@@ -448,7 +688,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
448
688
  const disconnectListener = jest.fn();
449
689
  harness.emitter.on(TRANSPORT_EVENT.DEVICE_DISCONNECT, disconnectListener);
450
690
 
451
- await harness.transport.acquire({ uuid: harness.uuid });
691
+ await harness.transport.acquire({ uuid: harness.uuid, expectedProtocol: 'V2' });
452
692
  harness.emitDisconnect();
453
693
  await harness.transport.disconnect(harness.uuid);
454
694
 
@@ -482,7 +722,6 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
482
722
  configureProtocolV2BleTuning({ iosPacketLength: 20 });
483
723
 
484
724
  await transport.writeProtocolV2Frame(
485
- 'device-uuid',
486
725
  bleTransport,
487
726
  new Uint8Array(30),
488
727
  context,
@@ -514,13 +753,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
514
753
  configureProtocolV2BleTuning({ iosPacketLength: 20 });
515
754
 
516
755
  await expect(
517
- transport.writeProtocolV2Frame(
518
- 'device-uuid',
519
- bleTransport,
520
- new Uint8Array(30),
521
- context,
522
- jest.fn()
523
- )
756
+ transport.writeProtocolV2Frame(bleTransport, new Uint8Array(30), context, jest.fn())
524
757
  ).rejects.toMatchObject({ errorCode: 205 });
525
758
  expect(writeWithoutResponse).toHaveBeenCalledTimes(1);
526
759
  });