@reown/appkit-controllers
Version:
#### 🔗 [Website](https://reown.com/appkit)
542 lines • 21 kB
JavaScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ChainController, ConnectionController, ConnectorController, StorageUtil } from '../../exports/index.js';
import { useAppKitAccount, useAppKitConnection, useAppKitConnections, useAppKitNetworkCore, useDisconnect } from '../../exports/react.js';
import { AssetUtil } from '../../exports/utils.js';
import { ConnectionControllerUtil } from '../../src/utils/ConnectionControllerUtil.js';
vi.mock('valtio', () => ({
useSnapshot: vi.fn()
}));
vi.mock('react', () => ({
useCallback: vi.fn(fn => fn),
useState: vi.fn(() => [0, vi.fn()])
}));
const { useSnapshot } = vi.mocked(await import('valtio'), true);
const mockedReact = vi.mocked(await import('react'), true);
describe('useAppKitNetwork', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('should return the correct network state', () => {
const mockNetwork = {
id: 1,
name: 'Ethereum',
assets: {
imageId: 'ethereum',
imageUrl: ''
},
caipNetworkId: 'eip155:1',
chainNamespace: 'eip155',
nativeCurrency: {
name: 'Ethereum',
decimals: 18,
symbol: 'ETH'
},
rpcUrls: {
default: {
http: ['']
}
}
};
// Mock the useSnapshot hook
useSnapshot.mockReturnValue({
activeCaipNetwork: mockNetwork
});
const { caipNetwork, chainId } = useAppKitNetworkCore();
expect(caipNetwork).toBe(mockNetwork);
expect(chainId).toBe(1);
expect(useSnapshot).toHaveBeenCalledWith(ChainController.state);
});
});
describe('useAppKitAccount', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('should return the correct account state when disconnected', () => {
useSnapshot.mockReturnValueOnce({
activeChain: 'eip155',
chains: new Map([
[
'eip155',
{
accountState: {
address: undefined,
caipAddress: undefined,
allAccounts: [],
status: 'disconnected'
}
}
]
])
});
const result = useAppKitAccount();
expect(result).toEqual({
allAccounts: [],
address: undefined,
caipAddress: undefined,
isConnected: false,
status: 'disconnected',
embeddedWalletInfo: undefined
});
});
it('should return the correct account state when connected', () => {
const mockCaipAddress = 'eip155:1:0x123...';
const mockPlainAddress = '0x123...';
useSnapshot.mockReturnValueOnce({
activeChain: 'eip155',
chains: new Map([
[
'eip155',
{
accountState: {
address: mockPlainAddress,
caipAddress: mockCaipAddress,
allAccounts: [],
status: 'connected'
}
}
]
])
});
const result = useAppKitAccount();
expect(result).toEqual({
allAccounts: [],
address: mockPlainAddress,
caipAddress: mockCaipAddress,
isConnected: true,
status: 'connected',
embeddedWalletInfo: undefined
});
});
it('should return correct embedded wallet info when connected with social provider', () => {
const mockCaipAddress = 'eip155:1:0x123...';
const mockPlainAddress = '0x123...';
const authConnector = {
id: 'ID_AUTH',
name: 'ID Auth',
imageUrl: 'https://example.com/id-auth.png'
};
vi.spyOn(ConnectorController, 'getAuthConnector').mockReturnValue(authConnector);
vi.spyOn(StorageUtil, 'getConnectedConnectorId').mockReturnValue('ID_AUTH');
vi.spyOn(StorageUtil, 'getConnectedSocialUsername').mockReturnValue('test-username');
useSnapshot.mockReturnValueOnce({
activeChain: 'eip155',
chains: new Map([
[
'eip155',
{
accountState: {
address: mockPlainAddress,
caipAddress: mockCaipAddress,
allAccounts: [],
status: 'connected',
preferredAccountTypes: { eip155: 'eoa' },
socialProvider: 'google',
smartAccountDeployed: false,
user: {
email: 'email@email.test'
}
}
}
]
])
});
const result = useAppKitAccount();
expect(result).toEqual({
allAccounts: [],
address: mockPlainAddress,
caipAddress: mockCaipAddress,
isConnected: true,
status: 'connected',
embeddedWalletInfo: {
user: {
email: 'email@email.test',
username: 'test-username'
},
authProvider: 'google',
accountType: 'eoa',
isSmartAccountDeployed: false
}
});
});
it('should return account state with namespace parameter', async () => {
vi.spyOn(ConnectorController, 'state', 'get').mockReturnValue({
...ConnectorController.state,
allConnectors: [{}],
connected: true,
activeConnector: {}
});
const mockCaipAddress = 'eip155:1:0x123...';
const mockPlainAddress = '0x123...';
useSnapshot.mockReturnValueOnce({
activeChain: 'eip155',
chains: new Map([
[
'eip155',
{
accountState: {
address: mockPlainAddress,
caipAddress: mockCaipAddress,
allAccounts: [],
status: 'connected'
}
}
]
])
});
const result = useAppKitAccount({ namespace: 'solana' });
expect(result).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'
};
beforeEach(() => {
vi.resetAllMocks();
mockedReact.useState.mockReturnValue([0, vi.fn()]);
mockedReact.useCallback.mockImplementation(fn => fn);
});
it('should return formatted connections and storage connections', () => {
useSnapshot
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({ activeChain: 'eip155' });
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 result = useAppKitConnections();
expect(result).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', () => {
useSnapshot
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({ activeChain: 'eip155' });
vi.spyOn(ConnectionControllerUtil, 'getConnectionsData').mockReturnValue({
connections: [],
recentConnections: []
});
useAppKitConnections('solana');
expect(ConnectionControllerUtil.getConnectionsData).toHaveBeenCalledWith('solana');
});
it('should throw error when no namespace is found', () => {
useSnapshot
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({ activeChain: undefined });
expect(() => useAppKitConnections()).toThrow('No namespace found');
});
it('should handle empty connections', () => {
useSnapshot
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({})
.mockReturnValueOnce({ activeChain: 'eip155' });
vi.spyOn(ConnectionControllerUtil, 'getConnectionsData').mockReturnValue({
connections: [],
recentConnections: []
});
const result = useAppKitConnections();
expect(result).toEqual({
connections: [],
recentConnections: []
});
});
});
describe('useAppKitConnection', () => {
const mockConnection = {
connectorId: 'test-connector',
accounts: [{ address: '0x123...', type: 'eoa' }],
caipNetwork: {}
};
const mockOnSuccess = vi.fn();
const mockOnError = vi.fn();
beforeEach(() => {
vi.resetAllMocks();
mockedReact.useState.mockReturnValue([0, vi.fn()]);
mockedReact.useCallback.mockImplementation(fn => fn);
});
it('should return current connection and connection state', () => {
const mockConnections = new Map([['eip155', [mockConnection]]]);
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { eip155: 'test-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
const result = useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
});
expect(result.connection).toBe(mockConnection);
expect(result.isPending).toBe(false);
expect(typeof result.switchConnection).toBe('function');
expect(typeof result.deleteConnection).toBe('function');
});
it('should handle switching connection successfully', async () => {
const mockConnections = new Map([['eip155', [mockConnection]]]);
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { eip155: 'test-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
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 { switchConnection } = useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
});
await 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 mockConnections = new Map([['eip155', [mockConnection]]]);
const mockError = new Error('Connection failed');
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { eip155: 'test-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
const setIsSwitchingConnectionSpy = vi.spyOn(ConnectionController, 'setIsSwitchingConnection');
const switchConnectionSpy = vi
.spyOn(ConnectionController, 'switchConnection')
.mockRejectedValue(mockError);
const { switchConnection } = useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
});
await 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 mockConnections = new Map([['eip155', [mockConnection]]]);
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { eip155: 'test-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
const setIsSwitchingConnectionSpy = vi.spyOn(ConnectionController, 'setIsSwitchingConnection');
const switchConnectionSpy = vi
.spyOn(ConnectionController, 'switchConnection')
.mockRejectedValue('String error');
const { switchConnection } = useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
});
await 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', () => {
const mockConnections = new Map([['eip155', [mockConnection]]]);
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { eip155: 'test-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
const deleteAddressFromConnectionSpy = vi.spyOn(StorageUtil, 'deleteAddressFromConnection');
const { deleteConnection } = useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
});
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', () => {
const mockConnections = new Map([
['solana', [{ ...mockConnection, connectorId: 'solana-connector' }]]
]);
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { solana: 'solana-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
const result = useAppKitConnection({
namespace: 'solana',
onSuccess: mockOnSuccess,
onError: mockOnError
});
expect(result.connection).toEqual({ ...mockConnection, connectorId: 'solana-connector' });
});
it('should throw error when no namespace is found', () => {
useSnapshot
.mockReturnValueOnce({
connections: new Map(),
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: {}
})
.mockReturnValueOnce({ activeChain: undefined });
expect(() => useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
})).toThrow('No namespace found');
});
it('should return undefined connection when no matching connector found', () => {
const mockConnections = new Map([['eip155', [mockConnection]]]);
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { eip155: 'different-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
const result = useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
});
expect(result.connection).toBeUndefined();
});
it('should handle case-insensitive connector matching', () => {
const mockConnections = new Map([
['eip155', [{ ...mockConnection, connectorId: 'TEST-CONNECTOR' }]]
]);
useSnapshot
.mockReturnValueOnce({
connections: mockConnections,
isSwitchingConnection: false
})
.mockReturnValueOnce({
activeConnectorIds: { eip155: 'test-connector' }
})
.mockReturnValueOnce({ activeChain: 'eip155' });
const result = useAppKitConnection({
onSuccess: mockOnSuccess,
onError: mockOnError
});
expect(result.connection).toEqual({ ...mockConnection, connectorId: 'TEST-CONNECTOR' });
});
});
//# sourceMappingURL=react.test.js.map