@onekeyfe/hd-transport-web-device 1.2.8-alpha.13 → 1.2.8-alpha.130
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/__tests__/electron-ble-transport.test.ts +228 -7
- package/__tests__/webusb-identity-probe.test.ts +935 -0
- package/__tests__/webusb-protocol-cache.test.ts +301 -1
- package/__tests__/webusb-protocol-v2-timeout.test.ts +18 -5
- package/dist/electron-ble-transport.d.ts +4 -1
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +41 -4
- package/dist/index.js +688 -68
- package/dist/webusb.d.ts +38 -3
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +58 -18
- package/src/webusb.ts +758 -52
|
@@ -196,6 +196,33 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
196
196
|
});
|
|
197
197
|
});
|
|
198
198
|
|
|
199
|
+
test('passes the advertised color from the native bridge into the descriptor', async () => {
|
|
200
|
+
const nobleBle = createNobleBle();
|
|
201
|
+
nobleBle.enumerate.mockResolvedValueOnce([
|
|
202
|
+
{ id: 'colored-pro2-id', name: 'Pro 2 A1B2', bleColorCode: 'B' },
|
|
203
|
+
{ id: 'legacy-pro2-id', name: 'Pro 2 C3D4' },
|
|
204
|
+
] as never);
|
|
205
|
+
const bleTransport = configureTransport(nobleBle);
|
|
206
|
+
|
|
207
|
+
await expect(bleTransport.enumerate()).resolves.toEqual([
|
|
208
|
+
{
|
|
209
|
+
id: 'colored-pro2-id',
|
|
210
|
+
name: 'Pro 2 A1B2',
|
|
211
|
+
path: 'colored-pro2-id',
|
|
212
|
+
debug: false,
|
|
213
|
+
commType: 'electron-ble',
|
|
214
|
+
bleColorCode: 'B',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 'legacy-pro2-id',
|
|
218
|
+
name: 'Pro 2 C3D4',
|
|
219
|
+
path: 'legacy-pro2-id',
|
|
220
|
+
debug: false,
|
|
221
|
+
commType: 'electron-ble',
|
|
222
|
+
},
|
|
223
|
+
]);
|
|
224
|
+
});
|
|
225
|
+
|
|
199
226
|
test('does not treat an error envelope from a legacy preload as a successful connect', async () => {
|
|
200
227
|
const device = { id: 'legacy-error-id', name: 'OneKey Pro 2' };
|
|
201
228
|
const nobleBle = createNobleBle(device);
|
|
@@ -259,6 +286,192 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
259
286
|
expect(nobleBle.unsubscribe).not.toHaveBeenCalled();
|
|
260
287
|
});
|
|
261
288
|
|
|
289
|
+
test.each([false, true])(
|
|
290
|
+
'forwards the existing keepSession=%s release intent to native',
|
|
291
|
+
async keepSession => {
|
|
292
|
+
const device = { id: 'release-intent-pro2-id', name: 'OneKey Pro 2' };
|
|
293
|
+
const release = jest.fn().mockResolvedValue(undefined);
|
|
294
|
+
const nobleBle = { ...createNobleBle(device), release };
|
|
295
|
+
echoProtocolV2(nobleBle, device.id);
|
|
296
|
+
const bleTransport = configureTransport(nobleBle);
|
|
297
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
298
|
+
|
|
299
|
+
await bleTransport.release(device.id, false, keepSession);
|
|
300
|
+
|
|
301
|
+
expect(release).toHaveBeenCalledWith(device.id, keepSession);
|
|
302
|
+
expect(nobleBle.disconnect).not.toHaveBeenCalled();
|
|
303
|
+
expect(nobleBle.unsubscribe).not.toHaveBeenCalled();
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
test.each(['Pro 2', 'Neo'])(
|
|
308
|
+
'reuses the connection, subscription and V2 sequence for %s after release',
|
|
309
|
+
async name => {
|
|
310
|
+
const device = { id: 'retained-v2-id', name };
|
|
311
|
+
const release = jest.fn().mockResolvedValue(undefined);
|
|
312
|
+
const nobleBle = { ...createNobleBle(device), release };
|
|
313
|
+
echoProtocolV2(nobleBle, device.id);
|
|
314
|
+
const bleTransport = configureTransport(nobleBle);
|
|
315
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
316
|
+
await bleTransport.call(device.id, 'Ping', { message: 'before-release' });
|
|
317
|
+
const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
await bleTransport.release(device.id, false, true);
|
|
321
|
+
expect(bleTransport.getProtocolType(device.id)).toBe('V2');
|
|
322
|
+
expect(notifyCleanup).not.toHaveBeenCalled();
|
|
323
|
+
await expect(
|
|
324
|
+
bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
325
|
+
).resolves.toMatchObject({ uuid: device.id, protocolType: 'V2' });
|
|
326
|
+
await expect(
|
|
327
|
+
bleTransport.call(device.id, 'Ping', { message: 'after-release' })
|
|
328
|
+
).resolves.toMatchObject({ type: 'Success' });
|
|
329
|
+
|
|
330
|
+
expect(nobleBle.connect).toHaveBeenCalledTimes(1);
|
|
331
|
+
expect(nobleBle.subscribe).toHaveBeenCalledTimes(1);
|
|
332
|
+
expect(nobleBle.onNotification).toHaveBeenCalledTimes(1);
|
|
333
|
+
expect(nobleBle.onMtuChanged).toHaveBeenCalledTimes(1);
|
|
334
|
+
expect(release).toHaveBeenCalledWith(device.id, true);
|
|
335
|
+
expect(
|
|
336
|
+
nobleBle.write.mock.calls.map(([, hex]) => Number.parseInt(hex.slice(12, 14), 16))
|
|
337
|
+
).toEqual([1, 2, 3]);
|
|
338
|
+
} finally {
|
|
339
|
+
await bleTransport.disconnect(device.id);
|
|
340
|
+
}
|
|
341
|
+
expect(notifyCleanup).toHaveBeenCalledTimes(1);
|
|
342
|
+
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
test.each(['ordinary release', 'onclose', 'forced probe'])(
|
|
347
|
+
'does not retain renderer state for %s',
|
|
348
|
+
async mode => {
|
|
349
|
+
const device = { id: 'release-fallback-id', name: 'Pro 2' };
|
|
350
|
+
const nobleBle = createNobleBle(device);
|
|
351
|
+
echoProtocolV2(nobleBle, device.id);
|
|
352
|
+
const bleTransport = configureTransport(nobleBle);
|
|
353
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
354
|
+
const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
|
|
355
|
+
await bleTransport.release(device.id, mode === 'onclose', mode !== 'ordinary release');
|
|
356
|
+
|
|
357
|
+
const input = {
|
|
358
|
+
uuid: device.id,
|
|
359
|
+
expectedProtocol: 'V2' as const,
|
|
360
|
+
forceProtocolDetection: mode === 'forced probe',
|
|
361
|
+
};
|
|
362
|
+
await bleTransport.acquire(input);
|
|
363
|
+
|
|
364
|
+
expect(notifyCleanup).toHaveBeenCalledTimes(1);
|
|
365
|
+
expect(nobleBle.connect).toHaveBeenCalledTimes(2);
|
|
366
|
+
expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
|
|
367
|
+
await bleTransport.disconnect(device.id);
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
test.each([false, true])(
|
|
372
|
+
'rebuilds a retained link after a disconnect, including during lookup: %s',
|
|
373
|
+
async duringLookup => {
|
|
374
|
+
const device = { id: 'retained-drop-id', name: 'Pro 2' };
|
|
375
|
+
const nobleBle = createNobleBle(device);
|
|
376
|
+
echoProtocolV2(nobleBle, device.id);
|
|
377
|
+
const bleTransport = configureTransport(nobleBle);
|
|
378
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
379
|
+
await bleTransport.release(device.id, false, true);
|
|
380
|
+
const disconnectHandler = nobleBle.onDeviceDisconnected.mock.calls[0][0];
|
|
381
|
+
const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
|
|
382
|
+
const drop = () =>
|
|
383
|
+
disconnectHandler({ ...device, reason: EBleDisconnectReason.DeviceDisconnected });
|
|
384
|
+
if (duringLookup) {
|
|
385
|
+
nobleBle.getDevice.mockImplementationOnce(() => {
|
|
386
|
+
drop();
|
|
387
|
+
return Promise.resolve(device);
|
|
388
|
+
});
|
|
389
|
+
} else {
|
|
390
|
+
drop();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
394
|
+
await expect(bleTransport.call(device.id, 'Ping', {})).resolves.toMatchObject({
|
|
395
|
+
type: 'Success',
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
expect(notifyCleanup).toHaveBeenCalledTimes(1);
|
|
399
|
+
expect(nobleBle.connect).toHaveBeenCalledTimes(2);
|
|
400
|
+
expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
|
|
401
|
+
await bleTransport.disconnect(device.id);
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
test('disconnects a reused link on timeout without replaying the call', async () => {
|
|
406
|
+
const device = { id: 'retained-timeout-id', name: 'Pro 2' };
|
|
407
|
+
const nobleBle = createNobleBle(device);
|
|
408
|
+
echoProtocolV2(nobleBle, device.id);
|
|
409
|
+
const bleTransport = configureTransport(nobleBle);
|
|
410
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
411
|
+
await bleTransport.release(device.id, false, true);
|
|
412
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
413
|
+
nobleBle.write.mockImplementationOnce(() => Promise.resolve());
|
|
414
|
+
|
|
415
|
+
await expect(bleTransport.call(device.id, 'Ping', {}, { timeoutMs: 10 })).rejects.toMatchObject(
|
|
416
|
+
{
|
|
417
|
+
errorCode: HardwareErrorCode.BleTimeoutError,
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
expect(nobleBle.write).toHaveBeenCalledTimes(2);
|
|
422
|
+
expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
|
|
423
|
+
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
424
|
+
expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
|
|
425
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
426
|
+
expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
|
|
427
|
+
await bleTransport.disconnect(device.id);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test('invalidates a retained link when native release fails', async () => {
|
|
431
|
+
const device = { id: 'retained-release-failure-id', name: 'Pro 2' };
|
|
432
|
+
const release = jest.fn().mockRejectedValueOnce(new Error('IPC release failed'));
|
|
433
|
+
const nobleBle = { ...createNobleBle(device), release };
|
|
434
|
+
echoProtocolV2(nobleBle, device.id);
|
|
435
|
+
const bleTransport = configureTransport(nobleBle);
|
|
436
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
437
|
+
const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
|
|
438
|
+
|
|
439
|
+
await bleTransport.release(device.id, false, true);
|
|
440
|
+
|
|
441
|
+
expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
|
|
442
|
+
expect(notifyCleanup).toHaveBeenCalledTimes(1);
|
|
443
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
444
|
+
expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
|
|
445
|
+
await bleTransport.disconnect(device.id);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test('still cancels an in-flight V2 write when release requests retention', async () => {
|
|
449
|
+
const device = { id: 'active-release-id', name: 'Pro 2' };
|
|
450
|
+
const nobleBle = createNobleBle(device);
|
|
451
|
+
echoProtocolV2(nobleBle, device.id);
|
|
452
|
+
const bleTransport = configureTransport(nobleBle);
|
|
453
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
454
|
+
const started = createDeferred<void>();
|
|
455
|
+
const write = createDeferred<void>();
|
|
456
|
+
nobleBle.write.mockImplementationOnce(() => {
|
|
457
|
+
started.resolve();
|
|
458
|
+
return write.promise;
|
|
459
|
+
});
|
|
460
|
+
const result = bleTransport.call(device.id, 'Ping', {}).catch(error => error);
|
|
461
|
+
await started.promise;
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
const releasing = bleTransport.release(device.id, false, true);
|
|
465
|
+
write.resolve();
|
|
466
|
+
await releasing;
|
|
467
|
+
await expect(result).resolves.toMatchObject({ message: 'Electron BLE transport released' });
|
|
468
|
+
expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
|
|
469
|
+
} finally {
|
|
470
|
+
write.resolve();
|
|
471
|
+
await bleTransport.disconnect(device.id);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
262
475
|
test('forceNative disconnects a device the renderer no longer tracks', async () => {
|
|
263
476
|
const device = { id: 'presumed-dead-id', name: 'OneKey Pro 2' };
|
|
264
477
|
const nobleBle = createNobleBle(device);
|
|
@@ -461,6 +674,10 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
461
674
|
})
|
|
462
675
|
);
|
|
463
676
|
expect(transport.getProtocolType(device.id)).toBe('V1');
|
|
677
|
+
const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
|
|
678
|
+
await transport.release(device.id, false, true);
|
|
679
|
+
expect(transport.getProtocolType(device.id)).toBeUndefined();
|
|
680
|
+
expect(notifyCleanup).toHaveBeenCalledTimes(1);
|
|
464
681
|
await expect(transport.acquire({ uuid: device.id, expectedProtocol: 'V1' })).resolves.toEqual(
|
|
465
682
|
expect.objectContaining({
|
|
466
683
|
uuid: device.id,
|
|
@@ -561,9 +778,9 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
561
778
|
|
|
562
779
|
const transport = configureTransport(nobleBle);
|
|
563
780
|
|
|
564
|
-
await expect(transport.acquire({ uuid: device.id })).rejects.
|
|
565
|
-
|
|
566
|
-
);
|
|
781
|
+
await expect(transport.acquire({ uuid: device.id })).rejects.toMatchObject({
|
|
782
|
+
errorCode: HardwareErrorCode.TransportProtocolProbeFailed,
|
|
783
|
+
});
|
|
567
784
|
expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
|
|
568
785
|
expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
|
|
569
786
|
expect(transport.getProtocolType(device.id)).toBeUndefined();
|
|
@@ -629,7 +846,8 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
629
846
|
await expect(
|
|
630
847
|
transport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
631
848
|
).rejects.toMatchObject({
|
|
632
|
-
errorCode: HardwareErrorCode.
|
|
849
|
+
errorCode: HardwareErrorCode.TransportProtocolProbeFailed,
|
|
850
|
+
params: { expectedProtocol: 'V2' },
|
|
633
851
|
});
|
|
634
852
|
|
|
635
853
|
expect(nobleBle.connect).toHaveBeenCalledTimes(1);
|
|
@@ -645,12 +863,14 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
645
863
|
await expect(
|
|
646
864
|
transport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
647
865
|
).rejects.toMatchObject({
|
|
648
|
-
errorCode: HardwareErrorCode.
|
|
866
|
+
errorCode: HardwareErrorCode.TransportProtocolProbeFailed,
|
|
867
|
+
params: { expectedProtocol: 'V2' },
|
|
649
868
|
});
|
|
650
869
|
await expect(
|
|
651
870
|
transport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
652
871
|
).rejects.toMatchObject({
|
|
653
|
-
errorCode: HardwareErrorCode.
|
|
872
|
+
errorCode: HardwareErrorCode.TransportProtocolProbeFailed,
|
|
873
|
+
params: { expectedProtocol: 'V2' },
|
|
654
874
|
});
|
|
655
875
|
|
|
656
876
|
expect(transport.getProtocolType(device.id)).toBeUndefined();
|
|
@@ -755,7 +975,8 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
755
975
|
await expect(
|
|
756
976
|
transport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
|
|
757
977
|
).rejects.toMatchObject({
|
|
758
|
-
errorCode: HardwareErrorCode.
|
|
978
|
+
errorCode: HardwareErrorCode.TransportProtocolProbeFailed,
|
|
979
|
+
params: { expectedProtocol: 'V2' },
|
|
759
980
|
});
|
|
760
981
|
|
|
761
982
|
expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
|