@meshconnect/uwc-bridge-child 0.2.2 → 0.2.3-snapshot.0da4ecc
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/BridgeChild.d.ts +11 -3
- package/dist/BridgeChild.d.ts.map +1 -1
- package/dist/BridgeChild.js +269 -150
- package/dist/BridgeChild.js.map +1 -1
- package/dist/BridgeChild.test.js +348 -7
- package/dist/BridgeChild.test.js.map +1 -1
- package/dist/bridge-timing.d.ts +22 -0
- package/dist/bridge-timing.d.ts.map +1 -0
- package/dist/bridge-timing.js +41 -0
- package/dist/bridge-timing.js.map +1 -0
- package/dist/bridge-timing.test.d.ts +2 -0
- package/dist/bridge-timing.test.d.ts.map +1 -0
- package/dist/bridge-timing.test.js +27 -0
- package/dist/bridge-timing.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/BridgeChild.test.ts +393 -7
- package/src/BridgeChild.ts +320 -196
- package/src/bridge-timing.test.ts +33 -0
- package/src/bridge-timing.ts +52 -0
- package/src/index.ts +3 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,gFAAgF;AAChF,gDAAgD;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA"}
|
package/package.json
CHANGED
package/src/BridgeChild.test.ts
CHANGED
|
@@ -337,6 +337,319 @@ describe('BridgeChild', () => {
|
|
|
337
337
|
})
|
|
338
338
|
})
|
|
339
339
|
|
|
340
|
+
describe('Tron/TON discovery race (premature empty default)', () => {
|
|
341
|
+
it('does not default Tron to [] while a triggered discovery is still pending, then delivers the wallet once ready', async () => {
|
|
342
|
+
const mockTron = [
|
|
343
|
+
{
|
|
344
|
+
uuid: 'tron-tronlink',
|
|
345
|
+
name: 'tronLink',
|
|
346
|
+
injectedId: 'tronLink',
|
|
347
|
+
provider: { tronWeb: {} }
|
|
348
|
+
}
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
// Parent supports Tron but reports not-ready at first (e.g. TronLink's
|
|
352
|
+
// tronWeb hasn't finished injecting yet). EVM + Solana resolve instantly.
|
|
353
|
+
let tronReady = false
|
|
354
|
+
const api = {
|
|
355
|
+
eip6963WalletsReady: true,
|
|
356
|
+
walletStandardWalletsReady: true,
|
|
357
|
+
tonWalletsReady: true,
|
|
358
|
+
eip6963Wallets: [],
|
|
359
|
+
walletStandardWallets: [],
|
|
360
|
+
tonWallets: [],
|
|
361
|
+
tronWallets: mockTron,
|
|
362
|
+
get tronWalletsReady() {
|
|
363
|
+
return tronReady
|
|
364
|
+
},
|
|
365
|
+
discoverTronWallets: vi.fn(),
|
|
366
|
+
discoverTonWallets: vi.fn()
|
|
367
|
+
}
|
|
368
|
+
mockWrappedAPI = api as any
|
|
369
|
+
|
|
370
|
+
await createBridgeChildInIframe({ tronInjectedIds: ['tronLink'] })
|
|
371
|
+
|
|
372
|
+
// EVM + Solana resolve and several poll ticks pass while Tron is pending.
|
|
373
|
+
await vi.advanceTimersByTimeAsync(200)
|
|
374
|
+
|
|
375
|
+
// The placeholder must NOT default Tron to `[]` here: the injected
|
|
376
|
+
// connector's pollForBridgeTronWallets resolves on the first array it
|
|
377
|
+
// sees, so a premature `[]` makes it report "none found" and miss the
|
|
378
|
+
// real wallet marshalled below.
|
|
379
|
+
expect((window as any).tronWallets).toBeUndefined()
|
|
380
|
+
|
|
381
|
+
// Parent finishes Tron discovery (tronWeb now injected).
|
|
382
|
+
tronReady = true
|
|
383
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
384
|
+
|
|
385
|
+
expect((window as any).tronWallets).toHaveLength(1)
|
|
386
|
+
expect((window as any).tronWallets[0].uuid).toBe('tron-tronlink')
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('does not default TON to [] while a triggered discovery is still pending, then delivers the wallet once ready', async () => {
|
|
390
|
+
const mockTon = [
|
|
391
|
+
{
|
|
392
|
+
uuid: 'ton-trustwalletto',
|
|
393
|
+
name: 'Trust Wallet',
|
|
394
|
+
icon: '',
|
|
395
|
+
jsBridgeKey: 'trustwalletTon',
|
|
396
|
+
provider: createMockTonProvider('Trust Wallet')
|
|
397
|
+
}
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
let tonReady = false
|
|
401
|
+
const api = {
|
|
402
|
+
eip6963WalletsReady: true,
|
|
403
|
+
walletStandardWalletsReady: true,
|
|
404
|
+
tronWalletsReady: true,
|
|
405
|
+
eip6963Wallets: [],
|
|
406
|
+
walletStandardWallets: [],
|
|
407
|
+
tronWallets: [],
|
|
408
|
+
tonWallets: mockTon,
|
|
409
|
+
get tonWalletsReady() {
|
|
410
|
+
return tonReady
|
|
411
|
+
},
|
|
412
|
+
discoverTronWallets: vi.fn(),
|
|
413
|
+
discoverTonWallets: vi.fn()
|
|
414
|
+
}
|
|
415
|
+
mockWrappedAPI = api as any
|
|
416
|
+
|
|
417
|
+
await createBridgeChildInIframe({ tonJsBridgeKeys: ['trustwalletTon'] })
|
|
418
|
+
await vi.advanceTimersByTimeAsync(200)
|
|
419
|
+
|
|
420
|
+
// Same race as Tron: the placeholder must not pre-empt a pending TON
|
|
421
|
+
// discovery, or the connector's poll resolves empty and misses it.
|
|
422
|
+
expect((window as any).tonWallets).toBeUndefined()
|
|
423
|
+
|
|
424
|
+
tonReady = true
|
|
425
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
426
|
+
|
|
427
|
+
expect((window as any).tonWallets).toHaveLength(1)
|
|
428
|
+
expect((window as any).tonWallets[0].uuid).toBe('ton-trustwalletto')
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
it('still backstops Tron to [] at MAX_POLL_MS when the parent never reports ready', async () => {
|
|
432
|
+
const api = {
|
|
433
|
+
eip6963WalletsReady: true,
|
|
434
|
+
walletStandardWalletsReady: true,
|
|
435
|
+
tonWalletsReady: true,
|
|
436
|
+
eip6963Wallets: [],
|
|
437
|
+
walletStandardWallets: [],
|
|
438
|
+
tonWallets: [],
|
|
439
|
+
tronWallets: [],
|
|
440
|
+
tronWalletsReady: false,
|
|
441
|
+
discoverTronWallets: vi.fn(),
|
|
442
|
+
discoverTonWallets: vi.fn()
|
|
443
|
+
}
|
|
444
|
+
mockWrappedAPI = api as any
|
|
445
|
+
|
|
446
|
+
await createBridgeChildInIframe({ tronInjectedIds: ['tronLink'] })
|
|
447
|
+
|
|
448
|
+
// Pending → not prematurely defaulted.
|
|
449
|
+
await vi.advanceTimersByTimeAsync(500)
|
|
450
|
+
expect((window as any).tronWallets).toBeUndefined()
|
|
451
|
+
|
|
452
|
+
// Past MAX_POLL_MS (5s) the fillEmptyDefaults backstop resolves it to [].
|
|
453
|
+
await vi.advanceTimersByTimeAsync(5000)
|
|
454
|
+
expect((window as any).tronWallets).toEqual([])
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
it('recovers Tron when the parent exposes late (Comlink reads hang until then)', async () => {
|
|
458
|
+
// Reproduces the iframe-example race: BridgeParent is created on the
|
|
459
|
+
// iframe `load` event, so its Comlink endpoint isn't live when the child
|
|
460
|
+
// starts polling. A Comlink read to a not-yet-exposed parent never settles
|
|
461
|
+
// (comlink.ts requestResponseMessage has no timeout) — simulate with a
|
|
462
|
+
// getter returning a never-settling promise until the parent goes "live".
|
|
463
|
+
const mockTron = [
|
|
464
|
+
{
|
|
465
|
+
uuid: 'tron-tronlink',
|
|
466
|
+
name: 'tronLink',
|
|
467
|
+
injectedId: 'tronLink',
|
|
468
|
+
provider: { tronWeb: {} }
|
|
469
|
+
}
|
|
470
|
+
]
|
|
471
|
+
let live = false
|
|
472
|
+
const hang = () => new Promise(() => {})
|
|
473
|
+
const api = {
|
|
474
|
+
get eip6963WalletsReady() {
|
|
475
|
+
return live ? true : hang()
|
|
476
|
+
},
|
|
477
|
+
get walletStandardWalletsReady() {
|
|
478
|
+
return live ? true : hang()
|
|
479
|
+
},
|
|
480
|
+
get tronWalletsReady() {
|
|
481
|
+
return live ? true : hang()
|
|
482
|
+
},
|
|
483
|
+
get tonWalletsReady() {
|
|
484
|
+
return live ? true : hang()
|
|
485
|
+
},
|
|
486
|
+
eip6963Wallets: [],
|
|
487
|
+
walletStandardWallets: [],
|
|
488
|
+
tronWallets: mockTron,
|
|
489
|
+
tonWallets: [],
|
|
490
|
+
discoverTronWallets: vi.fn(),
|
|
491
|
+
discoverTonWallets: vi.fn()
|
|
492
|
+
}
|
|
493
|
+
mockWrappedAPI = api as any
|
|
494
|
+
|
|
495
|
+
await createBridgeChildInIframe({ tronInjectedIds: ['tronLink'] })
|
|
496
|
+
|
|
497
|
+
// Parent not live yet: reads time out and the loop retries — it must NOT
|
|
498
|
+
// hang (the bug). Tron stays undiscovered for now.
|
|
499
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
500
|
+
expect((window as any).tronWallets).toBeUndefined()
|
|
501
|
+
|
|
502
|
+
// Parent's BridgeParent finally exposes — the child must recover.
|
|
503
|
+
live = true
|
|
504
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
505
|
+
|
|
506
|
+
expect((window as any).tronWallets).toHaveLength(1)
|
|
507
|
+
expect((window as any).tronWallets[0].uuid).toBe('tron-tronlink')
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
it('recovers EIP-6963 when the parent exposes late (same raceParentRead path as Tron)', async () => {
|
|
511
|
+
// EVM/Solana use the same per-tick raceParentRead recovery as Tron, so a
|
|
512
|
+
// late-exposing parent must not strand them either.
|
|
513
|
+
const mockEip = [
|
|
514
|
+
{
|
|
515
|
+
uuid: 'mm',
|
|
516
|
+
name: 'MetaMask',
|
|
517
|
+
icon: '',
|
|
518
|
+
rdns: 'io.metamask',
|
|
519
|
+
provider: { request: vi.fn() }
|
|
520
|
+
}
|
|
521
|
+
]
|
|
522
|
+
let live = false
|
|
523
|
+
const hang = () => new Promise(() => {})
|
|
524
|
+
const api = {
|
|
525
|
+
get eip6963WalletsReady() {
|
|
526
|
+
return live ? true : hang()
|
|
527
|
+
},
|
|
528
|
+
get walletStandardWalletsReady() {
|
|
529
|
+
return live ? true : hang()
|
|
530
|
+
},
|
|
531
|
+
get tronWalletsReady() {
|
|
532
|
+
return live ? true : hang()
|
|
533
|
+
},
|
|
534
|
+
get tonWalletsReady() {
|
|
535
|
+
return live ? true : hang()
|
|
536
|
+
},
|
|
537
|
+
eip6963Wallets: mockEip,
|
|
538
|
+
walletStandardWallets: [],
|
|
539
|
+
tronWallets: [],
|
|
540
|
+
tonWallets: [],
|
|
541
|
+
discoverTronWallets: vi.fn(),
|
|
542
|
+
discoverTonWallets: vi.fn()
|
|
543
|
+
}
|
|
544
|
+
mockWrappedAPI = api as any
|
|
545
|
+
|
|
546
|
+
await createBridgeChildInIframe()
|
|
547
|
+
|
|
548
|
+
// Pre-expose: reads time out, the loop retries, nothing set yet.
|
|
549
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
550
|
+
expect((window as any).eip6963Wallets).toBeUndefined()
|
|
551
|
+
|
|
552
|
+
// Parent exposes — the child must recover EVM, not just Tron.
|
|
553
|
+
live = true
|
|
554
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
555
|
+
|
|
556
|
+
expect((window as any).eip6963Wallets).toHaveLength(1)
|
|
557
|
+
expect((window as any).eip6963Wallets[0].uuid).toBe('mm')
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
it('recovers TON when the parent exposes late (same raceParentRead path as Tron)', async () => {
|
|
561
|
+
const mockTon = [
|
|
562
|
+
{
|
|
563
|
+
uuid: 'ton-tonkeeper',
|
|
564
|
+
name: 'Tonkeeper',
|
|
565
|
+
icon: '',
|
|
566
|
+
jsBridgeKey: 'tonkeeper',
|
|
567
|
+
provider: createMockTonProvider('Tonkeeper')
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
let live = false
|
|
571
|
+
const hang = () => new Promise(() => {})
|
|
572
|
+
const api = {
|
|
573
|
+
get eip6963WalletsReady() {
|
|
574
|
+
return live ? true : hang()
|
|
575
|
+
},
|
|
576
|
+
get walletStandardWalletsReady() {
|
|
577
|
+
return live ? true : hang()
|
|
578
|
+
},
|
|
579
|
+
get tronWalletsReady() {
|
|
580
|
+
return live ? true : hang()
|
|
581
|
+
},
|
|
582
|
+
get tonWalletsReady() {
|
|
583
|
+
return live ? true : hang()
|
|
584
|
+
},
|
|
585
|
+
eip6963Wallets: [],
|
|
586
|
+
walletStandardWallets: [],
|
|
587
|
+
tronWallets: [],
|
|
588
|
+
tonWallets: mockTon,
|
|
589
|
+
discoverTronWallets: vi.fn(),
|
|
590
|
+
discoverTonWallets: vi.fn()
|
|
591
|
+
}
|
|
592
|
+
mockWrappedAPI = api as any
|
|
593
|
+
|
|
594
|
+
await createBridgeChildInIframe({ tonJsBridgeKeys: ['tonkeeper'] })
|
|
595
|
+
|
|
596
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
597
|
+
expect((window as any).tonWallets).toBeUndefined()
|
|
598
|
+
|
|
599
|
+
live = true
|
|
600
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
601
|
+
|
|
602
|
+
expect((window as any).tonWallets).toHaveLength(1)
|
|
603
|
+
expect((window as any).tonWallets[0].uuid).toBe('ton-tonkeeper')
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
it('recovers Solana when the parent exposes late (same raceParentRead path as Tron)', async () => {
|
|
607
|
+
const mockSolana = [
|
|
608
|
+
{
|
|
609
|
+
uuid: 'phantom-solana',
|
|
610
|
+
name: 'Phantom',
|
|
611
|
+
chains: ['solana:mainnet'],
|
|
612
|
+
features: ['standard:connect'],
|
|
613
|
+
adapter: {}
|
|
614
|
+
}
|
|
615
|
+
]
|
|
616
|
+
let live = false
|
|
617
|
+
const hang = () => new Promise(() => {})
|
|
618
|
+
const api = {
|
|
619
|
+
get eip6963WalletsReady() {
|
|
620
|
+
return live ? true : hang()
|
|
621
|
+
},
|
|
622
|
+
get walletStandardWalletsReady() {
|
|
623
|
+
return live ? true : hang()
|
|
624
|
+
},
|
|
625
|
+
get tronWalletsReady() {
|
|
626
|
+
return live ? true : hang()
|
|
627
|
+
},
|
|
628
|
+
get tonWalletsReady() {
|
|
629
|
+
return live ? true : hang()
|
|
630
|
+
},
|
|
631
|
+
eip6963Wallets: [],
|
|
632
|
+
walletStandardWallets: mockSolana,
|
|
633
|
+
tronWallets: [],
|
|
634
|
+
tonWallets: [],
|
|
635
|
+
discoverTronWallets: vi.fn(),
|
|
636
|
+
discoverTonWallets: vi.fn()
|
|
637
|
+
}
|
|
638
|
+
mockWrappedAPI = api as any
|
|
639
|
+
|
|
640
|
+
await createBridgeChildInIframe()
|
|
641
|
+
|
|
642
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
643
|
+
expect((window as any).walletStandardWallets).toBeUndefined()
|
|
644
|
+
|
|
645
|
+
live = true
|
|
646
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
647
|
+
|
|
648
|
+
expect((window as any).walletStandardWallets).toHaveLength(1)
|
|
649
|
+
expect((window as any).walletStandardWallets[0].name).toBe('Phantom')
|
|
650
|
+
})
|
|
651
|
+
})
|
|
652
|
+
|
|
340
653
|
describe('backward compatibility with older BridgeParent (no Tron)', () => {
|
|
341
654
|
it('should still set EVM and Solana wallets when parent has no Tron support', async () => {
|
|
342
655
|
const eip6963Wallets = [
|
|
@@ -358,7 +671,10 @@ describe('BridgeChild', () => {
|
|
|
358
671
|
}
|
|
359
672
|
]
|
|
360
673
|
|
|
361
|
-
//
|
|
674
|
+
// Defensive path: a parent whose Tron property access THROWS (e.g. a
|
|
675
|
+
// non-Comlink stub). Real old Comlink parents instead resolve a missing
|
|
676
|
+
// prop to undefined — see the MAX_POLL_MS backstop test below. Both end at
|
|
677
|
+
// tronWallets = [].
|
|
362
678
|
const oldParentAPI = {
|
|
363
679
|
eip6963WalletsReady: true,
|
|
364
680
|
walletStandardWalletsReady: true,
|
|
@@ -389,6 +705,33 @@ describe('BridgeChild', () => {
|
|
|
389
705
|
expect((window as any).tronWallets).toEqual([])
|
|
390
706
|
})
|
|
391
707
|
|
|
708
|
+
it('defaults Tron to [] via the MAX_POLL_MS backstop on a real old parent (missing props resolve to undefined, not throw)', async () => {
|
|
709
|
+
// Real Comlink resolves a GET on a missing property to `undefined` (only
|
|
710
|
+
// an APPLY on a missing method rejects). So an old parent without Tron
|
|
711
|
+
// support reports tronWalletsReady=undefined every tick; the child retries
|
|
712
|
+
// until MAX_POLL_MS, then fillEmptyDefaults sets [].
|
|
713
|
+
const oldParentAPI = {
|
|
714
|
+
eip6963WalletsReady: true,
|
|
715
|
+
walletStandardWalletsReady: true,
|
|
716
|
+
eip6963Wallets: [],
|
|
717
|
+
walletStandardWallets: [],
|
|
718
|
+
parentOrigin: 'https://parent.example.com'
|
|
719
|
+
// No tronWalletsReady / tronWallets / discoverTronWallets — a Comlink GET
|
|
720
|
+
// on a missing prop would resolve them to undefined.
|
|
721
|
+
}
|
|
722
|
+
mockWrappedAPI = oldParentAPI as any
|
|
723
|
+
|
|
724
|
+
await createBridgeChildInIframe({ tronInjectedIds: ['tronLink'] })
|
|
725
|
+
|
|
726
|
+
// Not prematurely defaulted while readiness stays undefined.
|
|
727
|
+
await vi.advanceTimersByTimeAsync(500)
|
|
728
|
+
expect((window as any).tronWallets).toBeUndefined()
|
|
729
|
+
|
|
730
|
+
// Past MAX_POLL_MS (5s) the backstop resolves it to [].
|
|
731
|
+
await vi.advanceTimersByTimeAsync(5000)
|
|
732
|
+
expect((window as any).tronWallets).toEqual([])
|
|
733
|
+
})
|
|
734
|
+
|
|
392
735
|
it('should set tron to empty array when discoverTronWallets throws', async () => {
|
|
393
736
|
const apiWithBrokenDiscover = {
|
|
394
737
|
eip6963WalletsReady: true,
|
|
@@ -435,13 +778,19 @@ describe('BridgeChild', () => {
|
|
|
435
778
|
// EVM and Solana should be set even though tron is not ready
|
|
436
779
|
expect((window as any).eip6963Wallets).toHaveLength(1)
|
|
437
780
|
expect((window as any).walletStandardWallets).toEqual([])
|
|
438
|
-
// Tron
|
|
439
|
-
|
|
781
|
+
// Tron stays undefined while the parent reports not-ready — we must NOT
|
|
782
|
+
// prematurely default it to []. The connector's poll resolves on the first
|
|
783
|
+
// array it sees, so an early [] makes it miss a wallet that arrives a
|
|
784
|
+
// moment later. tryMarshalTron writes it once the parent reports ready, or
|
|
785
|
+
// the MAX_POLL_MS backstop fills [] if it never does.
|
|
786
|
+
expect((window as any).tronWallets).toBeUndefined()
|
|
440
787
|
})
|
|
441
788
|
|
|
442
|
-
it('
|
|
443
|
-
//
|
|
444
|
-
//
|
|
789
|
+
it('calls discoverTronWallets once when Tron resolves on the first tick (re-issues per tick until tronDone otherwise)', async () => {
|
|
790
|
+
// Not a general invariant: the design re-issues discovery every tick until
|
|
791
|
+
// tronDone. Here Tron is ready immediately, so it marshals and sets
|
|
792
|
+
// tronDone on tick 1 — hence exactly one call. A slow parent would see one
|
|
793
|
+
// call per tick until ready.
|
|
445
794
|
let callCount = 0
|
|
446
795
|
const progressiveAPI = {
|
|
447
796
|
get eip6963WalletsReady() {
|
|
@@ -463,7 +812,8 @@ describe('BridgeChild', () => {
|
|
|
463
812
|
})
|
|
464
813
|
await vi.advanceTimersByTimeAsync(1000)
|
|
465
814
|
|
|
466
|
-
//
|
|
815
|
+
// Tron reports ready on the first tick, so tryMarshalTron marshals and
|
|
816
|
+
// sets tronDone — it stops re-issuing discoverTronWallets after that.
|
|
467
817
|
expect(progressiveAPI.discoverTronWallets).toHaveBeenCalledTimes(1)
|
|
468
818
|
})
|
|
469
819
|
})
|
|
@@ -708,6 +1058,42 @@ describe('BridgeChild', () => {
|
|
|
708
1058
|
manifestUrl: 'https://example.com'
|
|
709
1059
|
})
|
|
710
1060
|
})
|
|
1061
|
+
|
|
1062
|
+
it('recovers parentOrigin when the parent exposes late (same raceParentRead path as wallets)', async () => {
|
|
1063
|
+
// syncParentOrigin shares the late-expose hazard: a parentOrigin read posted
|
|
1064
|
+
// before the parent's Comlink.expose never settles. The retry loop must keep
|
|
1065
|
+
// trying until the parent comes up, then set window.uwcParentOrigin.
|
|
1066
|
+
let live = false
|
|
1067
|
+
const hang = () => new Promise(() => {})
|
|
1068
|
+
const api = {
|
|
1069
|
+
eip6963WalletsReady: true,
|
|
1070
|
+
walletStandardWalletsReady: true,
|
|
1071
|
+
tronWalletsReady: true,
|
|
1072
|
+
tonWalletsReady: true,
|
|
1073
|
+
eip6963Wallets: [],
|
|
1074
|
+
walletStandardWallets: [],
|
|
1075
|
+
tronWallets: [],
|
|
1076
|
+
tonWallets: [],
|
|
1077
|
+
get parentOrigin() {
|
|
1078
|
+
return live ? 'https://stake.com' : hang()
|
|
1079
|
+
},
|
|
1080
|
+
discoverTronWallets: vi.fn(),
|
|
1081
|
+
discoverTonWallets: vi.fn()
|
|
1082
|
+
}
|
|
1083
|
+
mockWrappedAPI = api as any
|
|
1084
|
+
|
|
1085
|
+
await createBridgeChildInIframe()
|
|
1086
|
+
|
|
1087
|
+
// Parent not live yet: the read times out and the loop retries — it must
|
|
1088
|
+
// NOT hang and must NOT set a value.
|
|
1089
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
1090
|
+
expect((window as any).uwcParentOrigin).toBeUndefined()
|
|
1091
|
+
|
|
1092
|
+
// Parent's BridgeParent finally exposes — the retry must recover.
|
|
1093
|
+
live = true
|
|
1094
|
+
await vi.advanceTimersByTimeAsync(600)
|
|
1095
|
+
expect((window as any).uwcParentOrigin).toBe('https://stake.com')
|
|
1096
|
+
})
|
|
711
1097
|
})
|
|
712
1098
|
|
|
713
1099
|
describe('checkWallets with all wallet types', () => {
|