Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 34 additions & 154 deletions packages/ui-components/src/__tests__/SelectToken.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('SelectToken', () => {
expect(getByText('Select a token...')).toBeInTheDocument();
});

it('calls setSelectToken and updates token info when input changes', async () => {
it('calls setSelectToken when token is selected from modal', async () => {
const user = userEvent.setup();
const mockGuiWithNoToken = {
...mockGui,
Expand All @@ -87,25 +87,29 @@ describe('SelectToken', () => {

(useGui as Mock).mockReturnValue(mockGuiWithNoToken);

const { getByTestId, getByRole } = render(SelectToken, {
const { getByText } = render(SelectToken, {
...mockProps
});

const customButton = getByTestId('custom-mode-button');
await user.click(customButton);
// Click the token selection button to open modal
const selectButton = getByText('Select a token...');
await user.click(selectButton);

const input = getByRole('textbox');
// Wait for modal to load tokens and then select one
await waitFor(() => {
expect(getByText('Test Token 1')).toBeInTheDocument();
});

await userEvent.clear(input);
await user.paste('0x456');
const firstToken = getByText('Test Token 1');
await user.click(firstToken);

await waitFor(() => {
expect(mockGuiWithNoToken.setSelectToken).toHaveBeenCalledWith('input', '0x456');
expect(mockGuiWithNoToken.setSelectToken).toHaveBeenCalledWith('input', '0x1234567890123456789012345678901234567890');
});
expect(mockStateUpdateCallback).toHaveBeenCalledTimes(1);
});

it('shows error message for invalid address, and removes the selectToken', async () => {
it('shows error message for invalid token selection', async () => {
const user = userEvent.setup();
const mockGuiWithError = {
...mockGui,
Expand All @@ -118,178 +122,54 @@ describe('SelectToken', () => {
...mockProps
});

const customButton = screen.getByTestId('custom-mode-button');
await user.click(customButton);
// Click the token selection button to open modal
const selectButton = screen.getByText('Select a token...');
await user.click(selectButton);

const input = screen.getByRole('textbox');
await userEvent.clear(input);
await user.paste('invalid');
// Wait for modal to load tokens and then select one
await waitFor(() => {
expect(screen.getByTestId('error')).toBeInTheDocument();
});
});

it('replaces the token and triggers state update twice if the token is already set', async () => {
const mockGuiWithTokenSet = {
...mockGui,
isSelectTokenSet: vi.fn().mockResolvedValue(true)
} as unknown as DotrainOrderGui;

(useGui as Mock).mockReturnValue(mockGuiWithTokenSet);

const user = userEvent.setup();

const { getByRole, getByTestId } = render(SelectToken, {
...mockProps
expect(screen.getByText('Test Token 1')).toBeInTheDocument();
});

const customButton = getByTestId('custom-mode-button');
await user.click(customButton);
const firstToken = screen.getByText('Test Token 1');
await user.click(firstToken);

const input = getByRole('textbox');
await userEvent.clear(input);
await user.paste('invalid');
await waitFor(() => {
expect(mockGuiWithTokenSet.setSelectToken).toHaveBeenCalled();
expect(mockStateUpdateCallback).toHaveBeenCalledTimes(1);
expect(screen.getByTestId('error')).toBeInTheDocument();
});
});

it('calls onSelectTokenSelect after input changes', async () => {
it('calls onSelectTokenSelect after token selection', async () => {
const user = userEvent.setup();
const { getByRole, getByTestId } = render(SelectToken, mockProps);
const { getByText } = render(SelectToken, mockProps);

const customButton = getByTestId('custom-mode-button');
await user.click(customButton);
// Click the token selection button to open modal
const selectButton = getByText('Select a token...');
await user.click(selectButton);

const input = getByRole('textbox');
// Wait for modal to load tokens and then select one
await waitFor(() => {
expect(getByText('Test Token 1')).toBeInTheDocument();
});

await userEvent.clear(input);
await user.paste('0x456');
const firstToken = getByText('Test Token 1');
await user.click(firstToken);

await waitFor(() => {
expect(mockProps.onSelectTokenSelect).toHaveBeenCalled();
});
});

describe('Dropdown Mode', () => {
describe('Token Selection', () => {
beforeEach(() => {
(useGui as Mock).mockReturnValue(mockGui);
});

it('shows dropdown and custom mode buttons when tokens are available', () => {
render(SelectToken, mockProps);

expect(screen.getByTestId('dropdown-mode-button')).toBeInTheDocument();
expect(screen.getByTestId('custom-mode-button')).toBeInTheDocument();
});

it('shows dropdown mode as active by default', () => {
render(SelectToken, mockProps);

const dropdownButton = screen.getByTestId('dropdown-mode-button');
const customButton = screen.getByTestId('custom-mode-button');

expect(dropdownButton).toHaveClass('border-blue-300');
expect(customButton).not.toHaveClass('border-blue-300');
});

it('switches to custom mode when custom button is clicked', async () => {
const user = userEvent.setup();
render(SelectToken, mockProps);

const customButton = screen.getByTestId('custom-mode-button');
await user.click(customButton);

expect(customButton).toHaveClass('border-blue-300');
expect(screen.getByTestId('dropdown-mode-button')).not.toHaveClass('border-blue-300');
});

it('shows TokenSelectionModal component in dropdown mode', () => {
it('shows token selection modal', () => {
render(SelectToken, mockProps);

expect(screen.getByText('Select a token...')).toBeInTheDocument();
});

it('shows custom input in custom mode', async () => {
const user = userEvent.setup();
render(SelectToken, mockProps);

const customButton = screen.getByTestId('custom-mode-button');
await user.click(customButton);

expect(screen.getByPlaceholderText('Enter token address (0x...)')).toBeInTheDocument();
});

it('clears state when switching from dropdown to custom mode', async () => {
const user = userEvent.setup();
const mockGuiNoToken = {
...mockGui,
getTokenInfo: vi.fn().mockResolvedValue({ value: null })
} as unknown as DotrainOrderGui;

(useGui as Mock).mockReturnValue(mockGuiNoToken);

render(SelectToken, {
...mockProps
});

const dropdownButton = screen.getByText('Select a token...');
await user.click(dropdownButton);

const firstToken = screen.getByText('Test Token 1');
await user.click(firstToken);

const customButton = screen.getByTestId('custom-mode-button');
await user.click(customButton);

const customInput = screen.getByPlaceholderText('Enter token address (0x...)');
expect(customInput).toHaveValue('');

expect(mockGuiNoToken.unsetSelectToken).toHaveBeenCalledWith('input');
});

it('clears state when switching from custom to dropdown mode', async () => {
const user = userEvent.setup();
render(SelectToken, mockProps);

const customButton = screen.getByTestId('custom-mode-button');
await user.click(customButton);

const customInput = screen.getByPlaceholderText('Enter token address (0x...)');
await user.type(customInput, '0x1234567890123456789012345678901234567890');

const dropdownButton = screen.getByTestId('dropdown-mode-button');
await user.click(dropdownButton);

expect(mockGui.unsetSelectToken).toHaveBeenCalledWith('input');
});

it('handles token selection from dropdown', async () => {
const user = userEvent.setup();
const mockGuiNoToken = {
...mockGui,
getTokenInfo: vi.fn().mockResolvedValue({ value: null })
} as unknown as DotrainOrderGui;

(useGui as Mock).mockReturnValue(mockGuiNoToken);

render(SelectToken, {
...mockProps
});

const dropdownButton = screen.getByText('Select a token...');
await user.click(dropdownButton);

const secondToken = screen.getByText('Another Token');
await user.click(secondToken);

expect(mockGuiNoToken.setSelectToken).toHaveBeenCalledWith(
'input',
'0x0987654321098765432109876543210987654321'
);
});

it('displays selected token info when token is selected', async () => {
mockGui.getTokenInfo = vi.fn().mockResolvedValue({
value: {
Expand Down
114 changes: 110 additions & 4 deletions packages/ui-components/src/__tests__/TokenSelectionModal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('TokenSelectionModal', () => {
const button = screen.getByRole('button');
await user.click(button);

expect(screen.getByPlaceholderText('Search tokens...')).toBeInTheDocument();
expect(screen.getByPlaceholderText('Search tokens or enter address (0x...)')).toBeInTheDocument();
});

it('loads tokens on mount', async () => {
Expand Down Expand Up @@ -155,7 +155,7 @@ describe('TokenSelectionModal', () => {
const button = screen.getByRole('button');
await user.click(button);

const searchInput = screen.getByPlaceholderText('Search tokens...');
const searchInput = screen.getByPlaceholderText('Search tokens or enter address (0x...)');
await user.type(searchInput, 'TEST');

await waitFor(() => {
Expand Down Expand Up @@ -183,7 +183,7 @@ describe('TokenSelectionModal', () => {
const button = screen.getByRole('button');
await user.click(button);

const searchInput = screen.getByPlaceholderText('Search tokens...');
const searchInput = screen.getByPlaceholderText('Search tokens or enter address (0x...)');
await user.type(searchInput, 'TEST');

expect(screen.getByText('Searching tokens...')).toBeInTheDocument();
Expand Down Expand Up @@ -252,8 +252,114 @@ describe('TokenSelectionModal', () => {
await user.click(button);

await waitFor(() => {
const searchInput = screen.getByPlaceholderText('Search tokens...');
const searchInput = screen.getByPlaceholderText('Search tokens or enter address (0x...)');
expect(searchInput).toHaveFocus();
});
});

it('validates custom token address when entered', async () => {
const mockGuiWithCustomToken = {
getAllTokens: vi.fn().mockResolvedValue({ value: [] }),
setSelectToken: vi.fn().mockResolvedValue(undefined),
getTokenInfo: vi.fn().mockResolvedValue({
value: {
address: '0x1234567890123456789012345678901234567890',
name: 'Custom Token',
symbol: 'CUSTOM',
decimals: 18
}
}),
unsetSelectToken: vi.fn().mockResolvedValue(undefined)
} as unknown as DotrainOrderGui;

(useGui as Mock).mockReturnValue(mockGuiWithCustomToken);

const user = userEvent.setup();
render(TokenSelectionModal, {
...defaultProps,
onSelect: mockOnSelect
});

const button = screen.getByRole('button');
await user.click(button);

const searchInput = screen.getByPlaceholderText('Search tokens or enter address (0x...)');
await user.type(searchInput, '0x1234567890123456789012345678901234567890');

await waitFor(() => {
expect(screen.getByText('Custom Token')).toBeInTheDocument();
expect(screen.getByText('Custom token (not in list)')).toBeInTheDocument();
});
});

it('shows error for invalid custom token address', async () => {
const mockGuiWithError = {
getAllTokens: vi.fn().mockResolvedValue({ value: [] }),
setSelectToken: vi.fn().mockRejectedValue(new Error('Invalid token address')),
unsetSelectToken: vi.fn().mockResolvedValue(undefined)
} as unknown as DotrainOrderGui;

(useGui as Mock).mockReturnValue(mockGuiWithError);

const user = userEvent.setup();
render(TokenSelectionModal, {
...defaultProps,
onSelect: mockOnSelect
});

const button = screen.getByRole('button');
await user.click(button);

const searchInput = screen.getByPlaceholderText('Search tokens or enter address (0x...)');
await user.type(searchInput, '0x1234567890123456789012345678901234567890');

await waitFor(() => {
expect(screen.getByText('Invalid token address')).toBeInTheDocument();
});
});

it('allows selection of custom token', async () => {
const mockGuiWithCustomToken = {
getAllTokens: vi.fn().mockResolvedValue({ value: [] }),
setSelectToken: vi.fn().mockResolvedValue(undefined),
getTokenInfo: vi.fn().mockResolvedValue({
value: {
address: '0x1234567890123456789012345678901234567890',
name: 'Custom Token',
symbol: 'CUSTOM',
decimals: 18
}
}),
unsetSelectToken: vi.fn().mockResolvedValue(undefined)
} as unknown as DotrainOrderGui;

(useGui as Mock).mockReturnValue(mockGuiWithCustomToken);

const user = userEvent.setup();
render(TokenSelectionModal, {
...defaultProps,
onSelect: mockOnSelect
});

const button = screen.getByRole('button');
await user.click(button);

const searchInput = screen.getByPlaceholderText('Search tokens or enter address (0x...)');
await user.type(searchInput, '0x1234567890123456789012345678901234567890');

await waitFor(() => {
expect(screen.getByText('Custom Token')).toBeInTheDocument();
});

const customTokenItem = screen.getByText('Custom Token');
await user.click(customTokenItem);

expect(mockOnSelect).toHaveBeenCalledWith({
key: 'temp-validation-token',
address: '0x1234567890123456789012345678901234567890',
name: 'Custom Token',
symbol: 'CUSTOM',
decimals: 18
});
});
});
Loading
Loading