import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import type { WalletMetadata } from '@meshconnect/uwc-types' import { discoverWallets } from './eip6963-discovery' // Mock window object const mockWindow = { addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), ethereum: undefined as any, UWCBridgeChildInitialized: false, eip6963Wallets: undefined as any } // Mock global window Object.defineProperty(globalThis, 'window', { value: mockWindow, writable: true }) describe('EIP-6963 Discovery', () => { beforeEach(() => { vi.clearAllMocks() // Reset window properties mockWindow.ethereum = undefined mockWindow.UWCBridgeChildInitialized = false mockWindow.eip6963Wallets = undefined // Clear module cache to ensure fresh imports vi.resetModules() }) afterEach(() => { vi.restoreAllMocks() }) describe('discoverWallets', () => { it('should return empty array in non-browser environment', async () => { // Mock non-browser environment Object.defineProperty(globalThis, 'window', { value: undefined, writable: true }) const result = await discoverWallets() expect(result).toEqual([]) // Restore window for other tests Object.defineProperty(globalThis, 'window', { value: mockWindow, writable: true }) }) it('should poll for bridge wallets when UWCBridgeChildInitialized is true', async () => { mockWindow.UWCBridgeChildInitialized = true const mockBridgeWallets = [ { uuid: 'bridge-wallet-1', name: 'Bridge Wallet', icon: 'bridge-icon', rdns: 'com.bridge.wallet', provider: { request: vi.fn() } } ] mockWindow.eip6963Wallets = mockBridgeWallets const result = await discoverWallets() expect(result).toEqual(mockBridgeWallets) }) it('should return empty array when bridge polling times out', async () => { mockWindow.UWCBridgeChildInitialized = true mockWindow.eip6963Wallets = undefined const result = await discoverWallets() expect(result).toEqual([]) }, 7000) it('should handle invalid bridge wallets array', async () => { mockWindow.UWCBridgeChildInitialized = true mockWindow.eip6963Wallets = 'invalid' as any const result = await discoverWallets() expect(result).toEqual([]) }, 7000) }) describe('getAvailableWallets', () => { const mockExpectedWallets: WalletMetadata[] = [ { id: 'metamask', name: 'MetaMask', metadata: { uuid: 'metamask-uuid', icon: 'metamask-icon', rdns: 'io.metamask' }, extensionInjectedProvider: { supportedNetworkIds: ['eip155:1'] as const, namespaceMetaData: { eip155: { eip155Name: 'MetaMask', injectedId: 'isMetaMask', supportsAddingNetworks: true, requiresUserApprovalOnNetworkSwitch: false } }, requiresUserApprovalOnNamespaceSwitch: false } } ] it('should add legacy provider wallet when detected', async () => { const mockDetectedWallets: any[] = [] const mockLegacyProvider = { request: vi.fn(), on: vi.fn(), removeListener: vi.fn(), isMetaMask: true } mockWindow.ethereum = mockLegacyProvider // Mock discoverWallets vi.doMock('./eip6963-discovery', async () => { const actual = await vi.importActual('./eip6963-discovery') return { ...actual, discoverWallets: vi.fn().mockResolvedValue(mockDetectedWallets) } }) const { getAvailableWallets } = await import('./eip6963-discovery') const result = await getAvailableWallets(mockExpectedWallets) expect(result).toHaveLength(1) expect(result[0].name).toBe('MetaMask') expect(result[0].provider).toBe(mockLegacyProvider) }) it('should not add legacy provider wallet if no matching expected wallet', async () => { const mockDetectedWallets: any[] = [] const mockLegacyProvider = { request: vi.fn(), on: vi.fn(), removeListener: vi.fn(), isMetaMask: true } mockWindow.ethereum = mockLegacyProvider // Mock discoverWallets vi.doMock('./eip6963-discovery', async () => { const actual = await vi.importActual('./eip6963-discovery') return { ...actual, discoverWallets: vi.fn().mockResolvedValue(mockDetectedWallets) } }) const { getAvailableWallets } = await import('./eip6963-discovery') const result = await getAvailableWallets([]) // Empty expected wallets expect(result).toHaveLength(0) }) it('should not add legacy provider wallet if injectedId does not match', async () => { const mockDetectedWallets: any[] = [] const mockLegacyProvider = { request: vi.fn(), on: vi.fn(), removeListener: vi.fn(), isCoinbaseWallet: true // Different wallet } mockWindow.ethereum = mockLegacyProvider // Mock discoverWallets vi.doMock('./eip6963-discovery', async () => { const actual = await vi.importActual('./eip6963-discovery') return { ...actual, discoverWallets: vi.fn().mockResolvedValue(mockDetectedWallets) } }) const { getAvailableWallets } = await import('./eip6963-discovery') const result = await getAvailableWallets(mockExpectedWallets) expect(result).toHaveLength(0) }) it('should handle missing metadata gracefully', async () => { const mockDetectedWallets: any[] = [] const mockLegacyProvider = { request: vi.fn(), on: vi.fn(), removeListener: vi.fn(), isMetaMask: true } mockWindow.ethereum = mockLegacyProvider const walletWithoutMetadata: WalletMetadata = { id: 'metamask', name: 'MetaMask', metadata: {}, // Empty metadata extensionInjectedProvider: { supportedNetworkIds: ['eip155:1'] as const, namespaceMetaData: { eip155: { eip155Name: 'MetaMask', injectedId: 'isMetaMask', supportsAddingNetworks: true, requiresUserApprovalOnNetworkSwitch: false } }, requiresUserApprovalOnNamespaceSwitch: false } } // Mock discoverWallets vi.doMock('./eip6963-discovery', async () => { const actual = await vi.importActual('./eip6963-discovery') return { ...actual, discoverWallets: vi.fn().mockResolvedValue(mockDetectedWallets) } }) const { getAvailableWallets } = await import('./eip6963-discovery') const result = await getAvailableWallets([walletWithoutMetadata]) expect(result).toHaveLength(1) expect(result[0].uuid).toBeTruthy() expect(result[0].name).toBe('MetaMask') expect(result[0].icon).toBe('') expect(result[0].rdns).toBe('') }) it('should handle missing namespace metadata gracefully', async () => { const mockDetectedWallets: any[] = [] const mockLegacyProvider = { request: vi.fn(), on: vi.fn(), removeListener: vi.fn(), isMetaMask: true } mockWindow.ethereum = mockLegacyProvider const walletWithoutNamespaceMetadata: WalletMetadata = { id: 'metamask', name: 'MetaMask', metadata: { uuid: 'metamask-uuid', icon: 'metamask-icon', rdns: 'io.metamask' }, extensionInjectedProvider: { supportedNetworkIds: ['eip155:1'] as const, namespaceMetaData: { eip155: { eip155Name: 'MetaMask', injectedId: '', // Empty injectedId supportsAddingNetworks: true, requiresUserApprovalOnNetworkSwitch: false } }, requiresUserApprovalOnNamespaceSwitch: false } } // Mock discoverWallets vi.doMock('./eip6963-discovery', async () => { const actual = await vi.importActual('./eip6963-discovery') return { ...actual, discoverWallets: vi.fn().mockResolvedValue(mockDetectedWallets) } }) const { getAvailableWallets } = await import('./eip6963-discovery') const result = await getAvailableWallets([walletWithoutNamespaceMetadata]) expect(result).toHaveLength(0) }) }) describe('Bridge wallet polling', () => { it('should resolve immediately when bridge wallets are available', async () => { mockWindow.UWCBridgeChildInitialized = true const mockBridgeWallets = [ { uuid: 'bridge-wallet-1', name: 'Bridge Wallet 1', icon: 'bridge-icon-1', rdns: 'com.bridge.wallet1', provider: { request: vi.fn() } } ] mockWindow.eip6963Wallets = mockBridgeWallets const result = await discoverWallets() expect(result).toEqual(mockBridgeWallets) }) }) })