diff --git a/packages/controllers/src/utils/ConnectUtil.ts b/packages/controllers/src/utils/ConnectUtil.ts index bab5c7eb44..cdf47096d0 100644 --- a/packages/controllers/src/utils/ConnectUtil.ts +++ b/packages/controllers/src/utils/ConnectUtil.ts @@ -62,7 +62,9 @@ export const ConnectUtil = { */ getWalletConnectWallets(wcAllWallets: WcWallet[], wcSearchWallets: WcWallet[]) { if (wcSearchWallets.length > 0) { - return wcSearchWallets.map(w => this.mapWalletToWalletItem(w)) + return WalletUtil.filterAndFlagWallets(wcSearchWallets).map(w => + this.mapWalletToWalletItem(w) + ) } return WalletUtil.getWalletConnectWallets(wcAllWallets).map(w => this.mapWalletToWalletItem(w)) diff --git a/packages/controllers/src/utils/WalletUtil.ts b/packages/controllers/src/utils/WalletUtil.ts index fe5e79aef0..9677f51732 100644 --- a/packages/controllers/src/utils/WalletUtil.ts +++ b/packages/controllers/src/utils/WalletUtil.ts @@ -184,6 +184,13 @@ export const WalletUtil = { return wallets.map((w, index) => ({ ...w, display_index: index })) }, + filterAndFlagWallets(wallets: WcWallet[]) { + const withInstalled = WalletUtil.markWalletsAsInstalled(wallets) + const filtered = WalletUtil.filterWalletsByWcSupport(withInstalled) + + return WalletUtil.markWalletsWithDisplayIndex(filtered) + }, + /** * Filters wallets based on WalletConnect support and platform requirements. * @@ -218,9 +225,7 @@ export const WalletUtil = { } const uniqueWallets = CoreHelperUtil.uniqueBy(wallets, 'id') - const walletsWithInstalled = WalletUtil.markWalletsAsInstalled(uniqueWallets) - const walletsByWcSupport = WalletUtil.filterWalletsByWcSupport(walletsWithInstalled) - return WalletUtil.markWalletsWithDisplayIndex(walletsByWcSupport) + return WalletUtil.filterAndFlagWallets(uniqueWallets) } } diff --git a/packages/controllers/tests/utils/ConnectUtil.test.ts b/packages/controllers/tests/utils/ConnectUtil.test.ts new file mode 100644 index 0000000000..4112be3c15 --- /dev/null +++ b/packages/controllers/tests/utils/ConnectUtil.test.ts @@ -0,0 +1,135 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { + ConnectionController, + ConnectorController, + OptionsController +} from '../../exports/index.js' +import { AssetUtil } from '../../src/utils/AssetUtil.js' +import { ConnectUtil } from '../../src/utils/ConnectUtil.js' +import { CoreHelperUtil } from '../../src/utils/CoreHelperUtil.js' +import type { WcWallet } from '../../src/utils/TypeUtil.js' + +// -- Helpers ------------------------------------------------------------------ +function createMockWcWallet(overrides: Partial = {}): WcWallet { + return { + id: 'wallet-id', + name: 'Test Wallet', + supports_wc: true, + mobile_link: 'testwalletapp://', + ...overrides + } +} + +// -- Tests -------------------------------------------------------------------- +describe('ConnectUtil', () => { + describe('getWalletConnectWallets', () => { + beforeEach(() => { + vi.restoreAllMocks() + ConnectorController.state.connectors = [] + OptionsController.state.featuredWalletIds = undefined + ConnectionController.state.wcBasic = false + vi.spyOn(AssetUtil, 'getWalletImageUrl').mockReturnValue('') + }) + + it('should filter out wallets without wc support from search results on mobile', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(true) + + const walletWithWc = createMockWcWallet({ + id: 'wc-wallet', + name: 'WC Wallet', + supports_wc: true + }) + const walletWithoutWc = createMockWcWallet({ + id: 'no-wc-wallet', + name: 'No WC Wallet', + supports_wc: false, + mobile_link: 'noWcWalletapp://' + }) + + const result = ConnectUtil.getWalletConnectWallets([], [walletWithWc, walletWithoutWc]) + + expect(result.map(w => w.id)).toEqual(['wc-wallet']) + }) + + it('should include all search results on desktop when wcBasic is false', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false) + ConnectionController.state.wcBasic = false + + const walletWithWc = createMockWcWallet({ + id: 'wc-wallet', + name: 'WC Wallet', + supports_wc: true + }) + const walletWithoutWc = createMockWcWallet({ + id: 'no-wc-wallet', + name: 'No WC Wallet', + supports_wc: false + }) + + const result = ConnectUtil.getWalletConnectWallets([], [walletWithWc, walletWithoutWc]) + + expect(result.map(w => w.id)).toEqual(['wc-wallet', 'no-wc-wallet']) + }) + + it('should filter out wallets without wc support on desktop when wcBasic is true', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false) + ConnectionController.state.wcBasic = true + + const walletWithWc = createMockWcWallet({ + id: 'wc-wallet', + name: 'WC Wallet', + supports_wc: true + }) + const walletWithoutWc = createMockWcWallet({ + id: 'no-wc-wallet', + name: 'No WC Wallet', + supports_wc: false + }) + + const result = ConnectUtil.getWalletConnectWallets([], [walletWithWc, walletWithoutWc]) + + expect(result.map(w => w.id)).toEqual(['wc-wallet']) + }) + + it('should preserve order of search results when no installed or featured wallets', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false) + + const wallets = [ + createMockWcWallet({ id: 'wallet-0', name: 'Wallet 0' }), + createMockWcWallet({ id: 'wallet-1', name: 'Wallet 1' }), + createMockWcWallet({ id: 'wallet-2', name: 'Wallet 2' }) + ] + + const result = ConnectUtil.getWalletConnectWallets([], wallets) + + expect(result).toHaveLength(3) + expect(result[0]?.id).toBe('wallet-0') + expect(result[1]?.id).toBe('wallet-1') + expect(result[2]?.id).toBe('wallet-2') + }) + + it('should sort installed wallets first in search results', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false) + ConnectorController.state.connectors = [ + { + id: 'wallet-2-connector', + name: 'Wallet 2', + type: 'ANNOUNCED', + chain: 'eip155', + info: { rdns: 'com.wallet2' } + } + ] as any + + const wallets = [ + createMockWcWallet({ id: 'wallet-0', name: 'Wallet 0' }), + createMockWcWallet({ id: 'wallet-1', name: 'Wallet 1' }), + createMockWcWallet({ id: 'wallet-2', name: 'Wallet 2', rdns: 'com.wallet2' }) + ] + + const result = ConnectUtil.getWalletConnectWallets([], wallets) + + expect(result[0]?.id).toBe('wallet-2') + }) + }) +}) diff --git a/packages/controllers/tests/utils/WalletUtil.test.ts b/packages/controllers/tests/utils/WalletUtil.test.ts new file mode 100644 index 0000000000..80bcb7b246 --- /dev/null +++ b/packages/controllers/tests/utils/WalletUtil.test.ts @@ -0,0 +1,75 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { + ConnectionController, + ConnectorController, + OptionsController +} from '../../exports/index.js' +import { CoreHelperUtil } from '../../src/utils/CoreHelperUtil.js' +import type { WcWallet } from '../../src/utils/TypeUtil.js' +import { WalletUtil } from '../../src/utils/WalletUtil.js' + +// -- Helpers ------------------------------------------------------------------ +function createMockWcWallet(overrides: Partial = {}): WcWallet { + return { + id: 'wallet-id', + name: 'Test Wallet', + supports_wc: true, + mobile_link: 'testwalletapp://', + ...overrides + } +} + +// -- Tests -------------------------------------------------------------------- +describe('WalletUtil', () => { + describe('filterAndFlagWallets', () => { + beforeEach(() => { + vi.restoreAllMocks() + ConnectorController.state.connectors = [] + OptionsController.state.featuredWalletIds = undefined + ConnectionController.state.wcBasic = false + }) + + it('should filter out wallets without wc support on mobile', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(true) + + const wallets = [ + createMockWcWallet({ id: 'wc-wallet', supports_wc: true }), + createMockWcWallet({ id: 'no-wc-wallet', supports_wc: false }) + ] + + const result = WalletUtil.filterAndFlagWallets(wallets) + + expect(result.map(w => w.id)).toEqual(['wc-wallet']) + }) + + it('should pass through all wallets on desktop', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false) + + const wallets = [ + createMockWcWallet({ id: 'wc-wallet', supports_wc: true }), + createMockWcWallet({ id: 'no-wc-wallet', supports_wc: false }) + ] + + const result = WalletUtil.filterAndFlagWallets(wallets) + + expect(result.map(w => w.id)).toEqual(['wc-wallet', 'no-wc-wallet']) + }) + + it('should assign display_index to each wallet', () => { + vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false) + + const wallets = [ + createMockWcWallet({ id: 'wallet-0' }), + createMockWcWallet({ id: 'wallet-1' }), + createMockWcWallet({ id: 'wallet-2' }) + ] + + const result = WalletUtil.filterAndFlagWallets(wallets) + + expect(result[0]?.display_index).toBe(0) + expect(result[1]?.display_index).toBe(1) + expect(result[2]?.display_index).toBe(2) + }) + }) +})