Skip to content

Commit 2f19790

Browse files
authored
test: Add unit test for AmountInput (#3772)
* test: Add unit test for AmountInput Signed-off-by: Emre Bogazliyanlioglu <emre@wormholelabs.xyz> * chore: Addressing PR review Signed-off-by: Emre Bogazliyanlioglu <emre@wormholelabs.xyz> --------- Signed-off-by: Emre Bogazliyanlioglu <emre@wormholelabs.xyz>
1 parent 253b98d commit 2f19790

6 files changed

Lines changed: 237 additions & 14 deletions

File tree

package-lock.json

Lines changed: 81 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
"@commitlint/config-conventional": "^19.8.1",
131131
"@eslint/js": "^9.32.0",
132132
"@playwright/test": "1.54.1",
133+
"@testing-library/jest-dom": "^6.8.0",
133134
"@testing-library/react": "^16.3.0",
134135
"@types/node": "^20",
135136
"@types/node-fetch": "^2.6.3",

src/contexts/wallet/WalletContext.test.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,22 @@ describe('WalletContext with InternalWalletProvider', () => {
9191
);
9292

9393
// Sending wallet (Ethereum)
94-
let sendingConnectPromise: Promise<any>;
95-
act(() => {
96-
sendingConnectPromise = result.current!.connectWallet(
94+
let sendingWallet: any;
95+
await act(async () => {
96+
const sendingConnectPromise = result.current!.connectWallet(
9797
'Ethereum',
9898
TransferWallet.SENDING,
9999
);
100-
});
101100

102-
act(() => {
103101
internalWalletProvider.onWalletSelected(
104102
mockSendingWallet as any,
105103
'Ethereum',
106104
TransferWallet.SENDING,
107105
);
106+
107+
sendingWallet = await sendingConnectPromise;
108108
});
109109

110-
const sendingWallet = await sendingConnectPromise!;
111110
expect(sendingWallet).toBe(mockSendingWallet);
112111

113112
expect(connectWallet).toHaveBeenCalledWith({
@@ -122,23 +121,22 @@ describe('WalletContext with InternalWalletProvider', () => {
122121
);
123122

124123
// Receiving wallet (Solana)
125-
let receivingConnectPromise: Promise<any>;
126-
act(() => {
127-
receivingConnectPromise = result.current!.connectWallet(
124+
let receivingWallet: any;
125+
await act(async () => {
126+
const receivingConnectPromise = result.current!.connectWallet(
128127
'Solana',
129128
TransferWallet.RECEIVING,
130129
);
131-
});
132130

133-
act(() => {
134131
internalWalletProvider.onWalletSelected(
135132
mockReceivingWallet as any,
136133
'Solana',
137134
TransferWallet.RECEIVING,
138135
);
136+
137+
receivingWallet = await receivingConnectPromise;
139138
});
140139

141-
const receivingWallet = await receivingConnectPromise!;
142140
expect(receivingWallet).toBe(mockReceivingWallet);
143141

144142
expect(connectReceivingWallet).toHaveBeenCalledWith({
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import React from 'react';
2+
import { describe, it, expect, vi, beforeEach } from 'vitest';
3+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
4+
import { Provider } from 'react-redux';
5+
import { ThemeProvider, createTheme } from '@mui/material';
6+
import { configureStore } from '@reduxjs/toolkit';
7+
8+
import { useGetTokens } from 'hooks/useGetTokens';
9+
import AmountInput from './AmountInput';
10+
import { dark } from 'theme';
11+
12+
const theme = createTheme({
13+
palette: dark as any,
14+
});
15+
16+
// Mock useGetTokens hook
17+
vi.mock('hooks/useGetTokens', () => ({
18+
useGetTokens: vi.fn(() => ({
19+
sourceToken: { symbol: 'USDC', decimals: 6 },
20+
destToken: { symbol: 'USDC', decimals: 6 },
21+
})),
22+
}));
23+
24+
const mockStore = configureStore({
25+
reducer: {
26+
transferInput: (
27+
state = {
28+
fromChain: 'Ethereum',
29+
isTransactionInProgress: false,
30+
},
31+
) => state,
32+
},
33+
});
34+
35+
const defaultProps = {
36+
value: '',
37+
debouncedValue: '',
38+
supportedSourceTokens: [],
39+
tokenBalance: null,
40+
receiveAmount: undefined,
41+
error: undefined,
42+
warning: undefined,
43+
onChange: vi.fn(),
44+
onDebouncedChange: vi.fn(),
45+
};
46+
47+
const AppWrapper =
48+
(store = mockStore) =>
49+
({ children }: { children: React.ReactNode }) =>
50+
(
51+
<Provider store={store}>
52+
<ThemeProvider theme={theme}>{children}</ThemeProvider>
53+
</Provider>
54+
);
55+
56+
describe('AmountInput', () => {
57+
beforeEach(() => {
58+
vi.clearAllMocks();
59+
});
60+
61+
it('renders and displays the provided value', () => {
62+
const props = { ...defaultProps, value: '100', debouncedValue: '100' };
63+
render(<AmountInput {...props} />, { wrapper: AppWrapper() });
64+
const input = screen.getByRole('textbox', { name: 'Amount input' });
65+
expect(input).toBeInTheDocument();
66+
expect(input).toHaveValue('100');
67+
});
68+
69+
it('calls onChange when input value changes', () => {
70+
render(<AmountInput {...defaultProps} />, { wrapper: AppWrapper() });
71+
const input = screen.getByRole('textbox', { name: 'Amount input' });
72+
fireEvent.change(input, { target: { value: '123' } });
73+
expect(defaultProps.onChange).toHaveBeenCalledWith('123');
74+
});
75+
76+
it('disables input when no source chain is selected', () => {
77+
const storeWithNoChain = configureStore({
78+
reducer: {
79+
transferInput: () => ({
80+
fromChain: undefined,
81+
isTransactionInProgress: false,
82+
}),
83+
},
84+
});
85+
86+
render(<AmountInput {...defaultProps} />, {
87+
wrapper: AppWrapper(storeWithNoChain),
88+
});
89+
const input = screen.getByRole('textbox', { name: 'Amount input' });
90+
expect(input).toBeDisabled();
91+
});
92+
93+
it('disables input when no source token is selected', () => {
94+
// Mock useGetTokens to return undefined for sourceToken
95+
vi.mocked(useGetTokens).mockReturnValueOnce({
96+
sourceToken: undefined,
97+
destToken: { symbol: 'USDC', decimals: 6 },
98+
} as any);
99+
100+
render(<AmountInput {...defaultProps} />, { wrapper: AppWrapper() });
101+
const input = screen.getByRole('textbox', { name: 'Amount input' });
102+
expect(input).toBeDisabled();
103+
});
104+
105+
it('disables input when transaction is in progress', () => {
106+
const storeWithTxInProgress = configureStore({
107+
reducer: {
108+
transferInput: () => ({
109+
fromChain: 'Ethereum',
110+
isTransactionInProgress: true,
111+
}),
112+
},
113+
});
114+
115+
render(<AmountInput {...defaultProps} />, {
116+
wrapper: AppWrapper(storeWithTxInProgress),
117+
});
118+
const input = screen.getByRole('textbox', { name: 'Amount input' });
119+
expect(input).toBeDisabled();
120+
});
121+
122+
it('rejects invalid input (non-numeric characters)', () => {
123+
render(<AmountInput {...defaultProps} />, { wrapper: AppWrapper() });
124+
const input = screen.getByRole('textbox', { name: 'Amount input' });
125+
126+
fireEvent.change(input, { target: { value: 'abc' } });
127+
expect(defaultProps.onChange).not.toHaveBeenCalled();
128+
});
129+
130+
it('formats large numbers with commas', async () => {
131+
const props = {
132+
...defaultProps,
133+
value: '1000000',
134+
debouncedValue: '1000000',
135+
};
136+
render(<AmountInput {...props} />, { wrapper: AppWrapper() });
137+
const input = screen.getByRole('textbox', { name: 'Amount input' });
138+
await waitFor(() => {
139+
expect(input).toHaveValue('1,000,000');
140+
});
141+
});
142+
});

vitest.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export default defineConfig({
55
test: {
66
globals: true,
77
environment: 'jsdom',
8+
setupFiles: ['./src/setupTests.ts'],
89
exclude: [
910
'**/node_modules/**',
1011
'**/dist/**',
@@ -13,6 +14,7 @@ export default defineConfig({
1314
'**/e2e/**',
1415
'**/tests/**',
1516
],
17+
watch: false,
1618
},
1719
resolve: {
1820
alias: {
@@ -30,6 +32,7 @@ export default defineConfig({
3032
public: path.resolve(__dirname, './public'),
3133
views: path.resolve(__dirname, './src/views'),
3234
exports: path.resolve(__dirname, './src/exports'),
35+
theme: path.resolve(__dirname, './src/theme'),
3336
},
3437
},
3538
});

0 commit comments

Comments
 (0)