import { describe, it, expect, beforeEach, vi } from 'vitest' import { TronWalletService } from './tron-wallet-service' import type { TronProvider } from '../../tron-discovery' import type { WalletMetadata } from '@meshconnect/uwc-types' // Mock the tron-discovery module vi.mock('../../tron-discovery', () => ({ getTronWallets: vi.fn() })) describe('TronWalletService', () => { let service: TronWalletService let mockProvider: TronProvider const createMockProvider = ( opts: { ready?: boolean; address?: string; hasRequest?: boolean } = {} ): TronProvider => ({ ready: opts.ready ?? true, request: opts.hasRequest !== false ? vi.fn() : undefined, tronWeb: { defaultAddress: { base58: opts.address || 'TTestAddress123', hex: '0x1234' }, trx: { sign: vi.fn(), signMessageV2: vi.fn(), sendRawTransaction: vi.fn() }, transactionBuilder: { sendTrx: vi.fn(), triggerSmartContract: vi.fn() }, toHex: vi.fn() } }) as any beforeEach(() => { vi.clearAllMocks() service = new TronWalletService() mockProvider = createMockProvider() }) describe('initializeDiscovery', () => { it('should fetch and store detected wallets', async () => { const { getTronWallets } = await import('../../tron-discovery') const mockWallets = [ { uuid: 'tron-tronlink', name: 'tronLink', injectedId: 'tronLink', provider: mockProvider } ] vi.mocked(getTronWallets).mockResolvedValue(mockWallets) await service.initializeDiscovery() expect(service.getDetectedWallets()).toEqual(mockWallets) }) it('should pass expected wallets to getTronWallets', async () => { const { getTronWallets } = await import('../../tron-discovery') vi.mocked(getTronWallets).mockResolvedValue([]) const expectedWallets: WalletMetadata[] = [ { id: 'tronlink', name: 'TronLink', metadata: {}, extensionInjectedProvider: { supportedNetworkIds: ['tron:0x2b6653dc'] as const, namespaceMetaData: { tron: { injectedId: 'tronLink' } }, requiresUserApprovalOnNamespaceSwitch: false } } ] await service.initializeDiscovery(expectedWallets) expect(getTronWallets).toHaveBeenCalledWith(expectedWallets) }) }) describe('getDetectedWallets', () => { it('should return empty array initially', () => { expect(service.getDetectedWallets()).toEqual([]) }) }) describe('findWalletByUuid', () => { it('should find wallet by uuid', async () => { const { getTronWallets } = await import('../../tron-discovery') const mockWallets = [ { uuid: 'tron-tronlink', name: 'tronLink', injectedId: 'tronLink', provider: mockProvider }, { uuid: 'tron-tokenpocket-tron', name: 'tokenpocket', injectedId: 'tokenpocket.tron', provider: createMockProvider() } ] vi.mocked(getTronWallets).mockResolvedValue(mockWallets) await service.initializeDiscovery() const wallet = service.findWalletByUuid('tron-tokenpocket-tron') expect(wallet).toEqual(mockWallets[1]) }) it('should return undefined for non-existent uuid', () => { const wallet = service.findWalletByUuid('non-existent') expect(wallet).toBeUndefined() }) }) describe('checkExistingConnection', () => { it('should return address if provider is ready with native defaultAddress', async () => { const provider = createMockProvider({ ready: true, address: 'TAddress1' }) const result = await service.checkExistingConnection(provider) expect(result).toBe('TAddress1') }) it('should return null if provider is not ready', async () => { const provider = createMockProvider({ ready: false }) const result = await service.checkExistingConnection(provider) expect(result).toBeNull() }) it('should return null on error', async () => { const provider = { ready: true, tronWeb: { get defaultAddress() { throw new Error('Access error') } } } as any const result = await service.checkExistingConnection(provider) expect(result).toBeNull() }) it('should use getDefaultAddress for bridge providers', async () => { const provider = { ready: true, tronWeb: { getDefaultAddress: vi .fn() .mockResolvedValue({ base58: 'TBridgeAddr1' }), trx: {}, transactionBuilder: {}, toHex: vi.fn() } } as any const result = await service.checkExistingConnection(provider) expect(provider.tronWeb.getDefaultAddress).toHaveBeenCalled() expect(result).toBe('TBridgeAddr1') }) it('should return null when defaultAddress is false', async () => { const provider = createMockProvider() provider.tronWeb.defaultAddress = { base58: false, hex: false } const result = await service.checkExistingConnection(provider) expect(result).toBeNull() }) }) describe('connect', () => { it('should connect successfully with request method', async () => { const provider = createMockProvider({ address: 'TConnectedAddr' }) const address = await service.connect(provider) expect(provider.request).toHaveBeenCalledWith({ method: 'tron_requestAccounts' }) expect(address).toBe('TConnectedAddr') expect(service.getAccount()).toBe('TConnectedAddr') expect(service.getConnectedProvider()).toBe(provider) }) it('should connect without request method (some wallets)', async () => { const provider = createMockProvider({ address: 'TAddr2', hasRequest: false }) const address = await service.connect(provider) expect(address).toBe('TAddr2') }) it('should throw error if no address returned', async () => { const provider = createMockProvider() provider.tronWeb.defaultAddress = { base58: false, hex: false } await expect(service.connect(provider)).rejects.toThrow( 'No address returned from Tron wallet' ) }) it('should use getDefaultAddress for bridge providers on connect', async () => { const provider = { ready: true, request: vi.fn(), tronWeb: { getDefaultAddress: vi .fn() .mockResolvedValue({ base58: 'TBridgeConnected' }), trx: {}, transactionBuilder: {}, toHex: vi.fn() } } as any const address = await service.connect(provider) expect(address).toBe('TBridgeConnected') }) }) describe('buildAvailableAddresses', () => { it('should build addresses for tron networks', () => { const supportedNetworkIds = ['tron:0x2b6653dc', 'tron:0xcd8690dc'] const address = 'TTestAddress123' const result = service.buildAvailableAddresses( supportedNetworkIds, address ) expect(result).toEqual([ { address: 'TTestAddress123', networkId: 'tron:0x2b6653dc' }, { address: 'TTestAddress123', networkId: 'tron:0xcd8690dc' } ]) }) it('should filter out non-tron networks', () => { const supportedNetworkIds = [ 'tron:0x2b6653dc', 'eip155:1', 'solana:mainnet' ] const address = 'TTestAddress123' const result = service.buildAvailableAddresses( supportedNetworkIds, address ) expect(result).toEqual([ { address: 'TTestAddress123', networkId: 'tron:0x2b6653dc' } ]) }) it('should return empty array for no tron networks', () => { const supportedNetworkIds = ['eip155:1', 'solana:mainnet'] const address = 'TTestAddress123' const result = service.buildAvailableAddresses( supportedNetworkIds, address ) expect(result).toEqual([]) }) }) describe('setConnectionState', () => { it('should update connection state', () => { service.setConnectionState(mockProvider, 'TAddr') expect(service.getConnectedProvider()).toBe(mockProvider) expect(service.getAccount()).toBe('TAddr') }) it('should allow clearing state', () => { service.setConnectionState(mockProvider, 'TAddr') service.setConnectionState(null, null) expect(service.getConnectedProvider()).toBeNull() expect(service.getAccount()).toBeNull() }) }) describe('disconnect', () => { it('should clear connection state', async () => { const provider = createMockProvider({ address: 'TAddr' }) await service.connect(provider) service.disconnect() expect(service.getConnectedProvider()).toBeNull() expect(service.getAccount()).toBeNull() }) }) describe('getTronWalletIdsFromExpectedWallets', () => { it('should extract injectedIds from expected wallets', () => { const wallets: WalletMetadata[] = [ { id: 'tronlink', name: 'TronLink', metadata: {}, extensionInjectedProvider: { supportedNetworkIds: ['tron:0x2b6653dc'] as const, namespaceMetaData: { tron: { injectedId: 'tronLink' } }, requiresUserApprovalOnNamespaceSwitch: false } }, { id: 'tokenpocket', name: 'TokenPocket', metadata: {}, extensionInjectedProvider: { supportedNetworkIds: ['tron:0x2b6653dc'] as const, namespaceMetaData: { tron: { injectedId: 'tokenpocket.tron' } }, requiresUserApprovalOnNamespaceSwitch: false } } ] const result = service.getTronWalletIdsFromExpectedWallets(wallets) expect(result).toEqual(['tronLink', 'tokenpocket.tron']) }) it('should filter out wallets without tron injectedId', () => { const wallets: WalletMetadata[] = [ { id: 'tronlink', name: 'TronLink', metadata: {}, extensionInjectedProvider: { supportedNetworkIds: ['tron:0x2b6653dc'] as const, namespaceMetaData: { tron: { injectedId: 'tronLink' } }, requiresUserApprovalOnNamespaceSwitch: false } }, { id: 'metamask', name: 'MetaMask', metadata: {}, extensionInjectedProvider: { supportedNetworkIds: ['eip155:1'] as const, namespaceMetaData: { eip155: { eip155Name: 'MetaMask', injectedId: 'isMetaMask', supportsAddingNetworks: true, requiresUserApprovalOnNetworkSwitch: false } }, requiresUserApprovalOnNamespaceSwitch: false } } ] const result = service.getTronWalletIdsFromExpectedWallets(wallets) expect(result).toEqual(['tronLink']) }) it('should return empty array for empty input', () => { const result = service.getTronWalletIdsFromExpectedWallets([]) expect(result).toEqual([]) }) it('should return empty array for undefined input', () => { const result = service.getTronWalletIdsFromExpectedWallets() expect(result).toEqual([]) }) }) })