UNPKG

@reown/appkit-controllers

Version:

#### 🔗 [Website](https://reown.com/appkit)

386 lines • 17.7 kB
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { createApp, nextTick } from 'vue'; import { ConstantsUtil } from '@reown/appkit-common'; import { AccountController, ChainController, ConnectionController, ConnectorController, StorageUtil } from '../../exports/index.js'; import { AssetUtil } from '../../exports/utils.js'; import { useAppKitAccount, useAppKitConnection, useAppKitConnections, useDisconnect } from '../../exports/vue.js'; import { ConnectionControllerUtil } from '../../src/utils/ConnectionControllerUtil.js'; import { mockChainControllerState, mockResetChainControllerState } from '../mocks/ChainController.js'; import { connectedAccountState, connectedWithEmbeddedWalletState, defaultAccountState, disconnectedAccountState } from '../mocks/useAppKitAccount.js'; export function withSetup(composable) { let result; const app = createApp({ setup() { result = composable(); return () => { }; } }); app.mount(document.createElement('div')); // @ts-expect-error ignore used before reassigned error return [result, app]; } describe('useAppKitAccount', () => { beforeAll(() => { mockChainControllerState(); }); afterAll(() => { mockResetChainControllerState(); }); it('should have default state when initialized', () => { const [state] = withSetup(() => useAppKitAccount()); expect(state.value).toEqual(defaultAccountState); }); it('should return the correct account state when connected', async () => { const [state] = withSetup(() => useAppKitAccount()); AccountController.setCaipAddress('eip155:1:0x123...', 'eip155'); AccountController.setStatus('connected', 'eip155'); await nextTick(); expect(state.value).toEqual(connectedAccountState); }); it('should return the correct account state when disconnected', async () => { const [state] = withSetup(() => useAppKitAccount()); ChainController.resetAccount('eip155'); AccountController.setStatus('disconnected', 'eip155'); await nextTick(); expect(state.value).toEqual(disconnectedAccountState); }); it('should return correct embedded wallet info when connected with social provider', async () => { const [state] = withSetup(() => useAppKitAccount()); AccountController.setCaipAddress('eip155:1:0x123...', 'eip155'); AccountController.setStatus('connected', 'eip155'); AccountController.setUser({ username: 'test', email: 'testuser@example.com' }, 'eip155'); AccountController.setSmartAccountDeployed(true, 'eip155'); AccountController.setPreferredAccountType('smartAccount', 'eip155'); const authConnector = { id: ConstantsUtil.CONNECTOR_ID.AUTH, type: 'ID_AUTH' }; ConnectorController.state.connectors = [authConnector]; vi.spyOn(StorageUtil, 'getConnectedConnectorId').mockReturnValue('ID_AUTH'); await nextTick(); expect(state.value).toEqual(connectedWithEmbeddedWalletState); }); it('should return account state with namespace parameter', async () => { ConnectorController.state.connectors = []; const [state] = withSetup(() => useAppKitAccount({ namespace: 'solana' })); await nextTick(); expect(state.value).toEqual({ allAccounts: [], address: undefined, caipAddress: undefined, isConnected: false, status: undefined, embeddedWalletInfo: undefined }); }); }); describe('useDisconnect', () => { it('should disconnect as expected', async () => { const disconnectSpy = vi.spyOn(ConnectionController, 'disconnect'); const { disconnect } = useDisconnect(); await disconnect(); expect(disconnectSpy).toHaveBeenCalled(); }); it('should disconnect for specific namespace as expected', async () => { const disconnectSpy = vi.spyOn(ConnectionController, 'disconnect'); const { disconnect } = useDisconnect(); await disconnect({ namespace: 'solana' }); expect(disconnectSpy).toHaveBeenCalledWith({ namespace: 'solana' }); }); }); describe('useAppKitConnections', () => { const mockConnection = { connectorId: 'test-connector', accounts: [{ address: '0x123...', type: 'eoa' }], caipNetwork: { id: 1, name: 'Ethereum', caipNetworkId: 'eip155:1', chainNamespace: 'eip155' } }; const mockFormattedConnection = { ...mockConnection, name: 'Test Connector', icon: 'connector-icon-url', networkIcon: 'network-icon-url' }; const mockConnector = { id: 'test-connector', type: 'WALLET_CONNECT', name: 'Test Connector', chain: 'eip155' }; beforeAll(() => { mockChainControllerState(); }); afterAll(() => { mockResetChainControllerState(); vi.restoreAllMocks(); }); it('should return formatted connections and storage connections', async () => { vi.spyOn(ConnectionControllerUtil, 'getConnectionsData').mockReturnValue({ connections: [mockConnection], recentConnections: [mockConnection] }); vi.spyOn(ConnectorController, 'getConnectorById').mockReturnValue(mockConnector); vi.spyOn(ConnectorController, 'getConnectorName').mockReturnValue('Test Connector'); vi.spyOn(AssetUtil, 'getConnectorImage').mockReturnValue('connector-icon-url'); vi.spyOn(AssetUtil, 'getNetworkImage').mockReturnValue('network-icon-url'); const [state] = withSetup(() => useAppKitConnections()); await nextTick(); expect(state.value).toEqual({ connections: [mockFormattedConnection], recentConnections: [mockFormattedConnection] }); expect(ConnectionControllerUtil.getConnectionsData).toHaveBeenCalledWith('eip155'); expect(ConnectorController.getConnectorById).toHaveBeenCalledWith('test-connector'); expect(AssetUtil.getConnectorImage).toHaveBeenCalled(); expect(AssetUtil.getNetworkImage).toHaveBeenCalledWith(mockConnection.caipNetwork); }); it('should use provided namespace instead of active chain', async () => { vi.spyOn(ConnectionControllerUtil, 'getConnectionsData').mockReturnValue({ connections: [], recentConnections: [] }); withSetup(() => useAppKitConnections('solana')); await nextTick(); expect(ConnectionControllerUtil.getConnectionsData).toHaveBeenCalledWith('solana'); }); it('should return empty state when no namespace is found', () => { vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue(undefined); const [state] = withSetup(() => useAppKitConnections()); expect(state.value.connections).toEqual([]); expect(state.value.recentConnections).toEqual([]); }); it('should handle empty connections', () => { vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue('eip155'); vi.spyOn(ConnectionControllerUtil, 'getConnectionsData').mockReturnValue({ connections: [], recentConnections: [] }); const [state] = withSetup(() => useAppKitConnections()); expect(state.value.connections).toEqual([]); expect(state.value.recentConnections).toEqual([]); }); it('should update when controller states change', async () => { const mockConnections = [mockConnection]; vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue('eip155'); vi.spyOn(ConnectionControllerUtil, 'getConnectionsData').mockReturnValue({ connections: mockConnections, recentConnections: [] }); const [state] = withSetup(() => useAppKitConnections()); await nextTick(); expect(state.value.connections).toHaveLength(1); vi.spyOn(ConnectionControllerUtil, 'getConnectionsData').mockReturnValue({ connections: [], recentConnections: [] }); await nextTick(); }); }); describe('useAppKitConnection', () => { const mockConnection = { connectorId: 'test-connector', accounts: [{ address: '0x123...', type: 'eoa' }], caipNetwork: { id: 1, name: 'Ethereum', caipNetworkId: 'eip155:1', chainNamespace: 'eip155' } }; const mockOnSuccess = vi.fn(); const mockOnError = vi.fn(); beforeAll(() => { mockChainControllerState(); }); afterAll(() => { mockResetChainControllerState(); vi.restoreAllMocks(); }); beforeEach(() => { vi.resetAllMocks(); vi.spyOn(ConnectionController.state, 'connections', 'get').mockReturnValue(new Map([['eip155', [mockConnection]]])); vi.spyOn(ConnectionController.state, 'isSwitchingConnection', 'get').mockReturnValue(false); vi.spyOn(ConnectorController.state, 'activeConnectorIds', 'get').mockReturnValue({ ...ConnectorController.state.activeConnectorIds, eip155: 'test-connector' }); vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue('eip155'); }); it('should return current connection and connection state', async () => { const mockConnections = new Map([['eip155', [mockConnection]]]); vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue('eip155'); vi.spyOn(ConnectionController.state, 'connections', 'get').mockReturnValue(mockConnections); vi.spyOn(ConnectorController.state, 'activeConnectorIds', 'get').mockReturnValue({ ...ConnectorController.state.activeConnectorIds, eip155: 'test-connector' }); vi.spyOn(ConnectionController.state, 'isSwitchingConnection', 'get').mockReturnValue(false); const [state] = withSetup(() => useAppKitConnection({ namespace: 'eip155' })); await nextTick(); expect(state.value.connection).toStrictEqual(mockConnection); expect(state.value.isPending).toBe(false); expect(typeof state.value.switchConnection).toBe('function'); expect(typeof state.value.deleteConnection).toBe('function'); }); it('should handle switching connection successfully', async () => { const setIsSwitchingConnectionSpy = vi.spyOn(ConnectionController, 'setIsSwitchingConnection'); const switchConnectionSpy = vi .spyOn(ConnectionController, 'switchConnection') .mockImplementation(async ({ onChange }) => { onChange?.({ address: '0x456...', namespace: 'eip155', hasSwitchedAccount: true, hasSwitchedWallet: false }); }); const [state] = withSetup(() => useAppKitConnection({ namespace: 'eip155', onSuccess: mockOnSuccess, onError: mockOnError })); await nextTick(); await state.value.switchConnection({ connection: mockConnection, address: '0x456...' }); expect(setIsSwitchingConnectionSpy).toHaveBeenCalledWith(true); expect(switchConnectionSpy).toHaveBeenCalledWith({ connection: mockConnection, address: '0x456...', namespace: 'eip155', onChange: expect.any(Function) }); expect(mockOnSuccess).toHaveBeenCalledWith({ address: '0x456...', namespace: 'eip155', hasSwitchedAccount: true, hasSwitchedWallet: false, hasDeletedWallet: false }); expect(setIsSwitchingConnectionSpy).toHaveBeenCalledWith(false); }); it('should handle switching connection error', async () => { const mockError = new Error('Connection failed'); const setIsSwitchingConnectionSpy = vi.spyOn(ConnectionController, 'setIsSwitchingConnection'); const switchConnectionSpy = vi .spyOn(ConnectionController, 'switchConnection') .mockRejectedValue(mockError); const [state] = withSetup(() => useAppKitConnection({ namespace: 'eip155', onSuccess: mockOnSuccess, onError: mockOnError })); await nextTick(); await state.value.switchConnection({ connection: mockConnection, address: '0x456...' }); expect(setIsSwitchingConnectionSpy).toHaveBeenCalledWith(true); expect(switchConnectionSpy).toHaveBeenCalled(); expect(mockOnError).toHaveBeenCalledWith(mockError); expect(setIsSwitchingConnectionSpy).toHaveBeenCalledWith(false); }); it('should handle non-error exceptions in switchConnection', async () => { const setIsSwitchingConnectionSpy = vi.spyOn(ConnectionController, 'setIsSwitchingConnection'); const switchConnectionSpy = vi .spyOn(ConnectionController, 'switchConnection') .mockRejectedValue('String error'); const [state] = withSetup(() => useAppKitConnection({ namespace: 'eip155', onSuccess: mockOnSuccess, onError: mockOnError })); await nextTick(); await state.value.switchConnection({ connection: mockConnection, address: '0x456...' }); expect(setIsSwitchingConnectionSpy).toHaveBeenCalledWith(true); expect(switchConnectionSpy).toHaveBeenCalled(); expect(mockOnError).toHaveBeenCalledWith(new Error('Something went wrong')); expect(setIsSwitchingConnectionSpy).toHaveBeenCalledWith(false); }); it('should handle deleting connection', async () => { const deleteAddressFromConnectionSpy = vi.spyOn(StorageUtil, 'deleteAddressFromConnection'); const [state] = withSetup(() => useAppKitConnection({ namespace: 'eip155', onSuccess: mockOnSuccess, onError: mockOnError })); await nextTick(); state.value.deleteConnection({ address: '0x123...', connectorId: 'test-connector' }); expect(deleteAddressFromConnectionSpy).toHaveBeenCalledWith({ connectorId: 'test-connector', address: '0x123...', namespace: 'eip155' }); expect(mockOnSuccess).toHaveBeenCalledWith({ address: '0x123...', namespace: 'eip155', hasSwitchedAccount: false, hasSwitchedWallet: false, hasDeletedWallet: true }); }); it('should use provided namespace instead of active chain', async () => { const solanaConnection = { ...mockConnection, connectorId: 'solana-connector' }; vi.spyOn(ConnectionController.state, 'connections', 'get').mockReturnValue(new Map([ ['eip155', [mockConnection]], ['solana', [solanaConnection]] ])); vi.spyOn(ConnectorController.state, 'activeConnectorIds', 'get').mockReturnValue({ ...ConnectorController.state.activeConnectorIds, eip155: 'test-connector', solana: 'solana-connector' }); const [state] = withSetup(() => useAppKitConnection({ namespace: 'solana', onSuccess: mockOnSuccess, onError: mockOnError })); await nextTick(); expect(state.value.connection).toEqual(solanaConnection); }); it('should return undefined connection when no namespace is found', () => { vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue(undefined); const [state] = withSetup(() => useAppKitConnection({ namespace: undefined, onSuccess: vi.fn(), onError: vi.fn() })); expect(state.value.connection).toBeUndefined(); expect(state.value.isPending).toBe(false); }); it('should return undefined connection when no matching connector found', () => { const mockConnections = new Map([['eip155', [mockConnection]]]); vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue('eip155'); vi.spyOn(ConnectionController.state, 'connections', 'get').mockReturnValue(mockConnections); vi.spyOn(ConnectorController.state, 'activeConnectorIds', 'get').mockReturnValue({ ...ConnectorController.state.activeConnectorIds, eip155: 'different-connector' }); const [state] = withSetup(() => useAppKitConnection({ namespace: 'eip155' })); expect(state.value.connection).toBeUndefined(); }); it('should handle case-insensitive connector matching', () => { const upperCaseConnection = { ...mockConnection, connectorId: 'TEST-CONNECTOR' }; const mockConnections = new Map([['eip155', [upperCaseConnection]]]); vi.spyOn(ChainController.state, 'activeChain', 'get').mockReturnValue('eip155'); vi.spyOn(ConnectionController.state, 'connections', 'get').mockReturnValue(mockConnections); vi.spyOn(ConnectorController.state, 'activeConnectorIds', 'get').mockReturnValue({ ...ConnectorController.state.activeConnectorIds, eip155: 'test-connector' }); const [state] = withSetup(() => useAppKitConnection({ namespace: 'eip155' })); expect(state.value.connection).toStrictEqual(upperCaseConnection); }); }); //# sourceMappingURL=vue.test.js.map