import { act, fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { FALLBACK_DEFAULT_MAX_SLIPPAGE } from '../constants';
import { SwapSettingsSlippageInput } from './SwapSettingsSlippageInput';
const mockSetLifecycleStatus = vi.fn();
const mockSendAnalytics = vi.fn();
let mockLifecycleStatus = {
statusName: 'init',
statusData: {
isMissingRequiredField: false,
maxSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
},
};
vi.mock('./SwapProvider', () => ({
useSwapContext: () => ({
updateLifecycleStatus: mockSetLifecycleStatus,
lifecycleStatus: mockLifecycleStatus,
config: { maxSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE },
}),
}));
vi.mock('../styles/theme', () => ({
cn: (...args: string[]) => args.join(' '),
}));
vi.mock('@/core/analytics/hooks/useAnalytics', () => ({
useAnalytics: () => ({
sendAnalytics: mockSendAnalytics,
}),
}));
describe('SwapSettingsSlippageInput', () => {
beforeEach(() => {
mockSetLifecycleStatus.mockClear();
mockSendAnalytics.mockClear();
mockLifecycleStatus = {
statusName: 'init',
statusData: {
isMissingRequiredField: false,
maxSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
},
};
});
it('renders with default props', () => {
render();
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
expect(screen.getByText('%')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Auto' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Custom' })).toBeInTheDocument();
});
it('applies custom className', () => {
const { container } = render(
,
);
expect(container.firstChild).toHaveClass('custom-class');
});
it('uses provided defaultSlippage', () => {
mockLifecycleStatus = {
statusName: 'error',
statusData: { isMissingRequiredField: false, maxSlippage: 1.5 },
};
render();
expect(screen.getByRole('textbox')).toHaveValue('1.5');
});
it('allows input changes in Custom mode but maintains default value', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), { target: { value: '2.5' } });
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
expect(mockSetLifecycleStatus).toHaveBeenCalledWith({
statusName: 'slippageChange',
statusData: {
maxSlippage: 2.5,
},
});
});
it('disables input in Auto mode', () => {
render();
expect(screen.getByRole('textbox')).toBeDisabled();
});
it('handles invalid input by maintaining default value', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), { target: { value: 'abc' } });
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
expect(mockSetLifecycleStatus).toHaveBeenCalledWith({
statusName: 'slippageChange',
statusData: {
maxSlippage: 0,
},
});
});
it('handles decimal input correctly', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), {
target: { value: '2.75' },
});
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
expect(mockSetLifecycleStatus).toHaveBeenCalledWith({
statusName: 'slippageChange',
statusData: {
maxSlippage: 2.75,
},
});
});
it('applies correct styles in Auto mode', () => {
render();
expect(screen.getByRole('button', { name: 'Auto' })).toHaveClass(
'cursor-pointer bg-ock-background hover:bg-ock-background-hover active:bg-ock-background-active focus:bg-ock-background-active text-ock-foreground font-ock font-semibold text-sm rounded-ock-inner flex-1 px-3 py-1 transition-colors bg-ock-background-inverse text-ock-primary shadow-ock-default',
);
expect(screen.getByRole('button', { name: 'Custom' })).toHaveClass(
'cursor-pointer bg-ock-background hover:bg-ock-background-hover active:bg-ock-background-active focus:bg-ock-background-active text-ock-foreground font-ock font-semibold text-sm rounded-ock-inner flex-1 px-3 py-1 transition-colors text-ock-foreground-muted',
);
expect(screen.getByRole('textbox').parentElement).toHaveClass('opacity-50');
});
it('applies correct styles in Custom mode', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
expect(screen.getByRole('button', { name: 'Auto' })).toHaveClass(
'cursor-pointer bg-ock-background hover:bg-ock-background-hover active:bg-ock-background-active focus:bg-ock-background-active text-ock-foreground font-ock font-semibold text-sm rounded-ock-inner flex-1 px-3 py-1 transition-colors text-ock-foreground-muted',
);
expect(screen.getByRole('button', { name: 'Custom' })).toHaveClass(
'cursor-pointer bg-ock-background hover:bg-ock-background-hover active:bg-ock-background-active focus:bg-ock-background-active text-ock-foreground font-ock font-semibold text-sm rounded-ock-inner flex-1 px-3 py-1 transition-colors bg-ock-background-inverse text-ock-primary shadow-ock-default',
);
expect(screen.getByRole('textbox').parentElement).not.toHaveClass(
'opacity-50',
);
});
it('handles empty input by maintaining default value', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), { target: { value: '' } });
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
expect(mockSetLifecycleStatus).toHaveBeenCalledWith({
statusName: 'slippageChange',
statusData: {
maxSlippage: 0,
},
});
});
it('uses lifecycleStatus maxSlippage when available', () => {
mockLifecycleStatus = {
statusName: 'updated',
statusData: {
isMissingRequiredField: false,
maxSlippage: 4.5,
},
};
render();
expect(screen.getByRole('textbox')).toHaveValue('4.5');
});
it('defaults to Custom mode when lifecycleStatus maxSlippage differs from default', () => {
mockLifecycleStatus = {
statusName: 'updated',
statusData: {
isMissingRequiredField: false,
maxSlippage: 4.5,
},
};
render();
expect(screen.getByRole('button', { name: 'Custom' })).toHaveClass(
'bg-ock-background-inverse text-ock-primary shadow-ock-default',
);
expect(screen.getByRole('textbox')).not.toBeDisabled();
});
it('handles non-numeric input by maintaining default value', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), { target: { value: 'abc' } });
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
expect(mockSetLifecycleStatus).toHaveBeenCalledWith({
statusName: 'slippageChange',
statusData: {
maxSlippage: 0,
},
});
});
it('updates slippage when switching from Custom to Auto', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), { target: { value: '5' } });
fireEvent.click(screen.getByRole('button', { name: 'Auto' }));
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
expect(mockSetLifecycleStatus).toHaveBeenLastCalledWith({
statusName: 'slippageChange',
statusData: {
maxSlippage: 5,
},
});
});
it('maintains default value when switching between Auto and Custom mode', () => {
render();
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
fireEvent.change(screen.getByRole('textbox'), { target: { value: '5' } });
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
fireEvent.click(screen.getByRole('button', { name: 'Auto' }));
expect(screen.getByRole('textbox')).toHaveValue(
FALLBACK_DEFAULT_MAX_SLIPPAGE.toString(),
);
});
it('can handle custom render prop', async () => {
render(
(
{slippageSetting}
)}
/>,
);
expect(screen.getByText('Custom Render')).toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: 'Set 1' }));
});
expect(mockSetLifecycleStatus).toHaveBeenLastCalledWith({
statusName: 'slippageChange',
statusData: {
maxSlippage: 1,
},
});
});
describe('analytics', () => {
it('sends analytics when slippage changes in Custom mode', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), {
target: { value: '2.5' },
});
expect(mockSendAnalytics).toHaveBeenCalledWith('swapSlippageChanged', {
previousSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
slippage: 2.5,
});
});
it('sends analytics when switching from Custom to Auto mode with different values', () => {
mockLifecycleStatus = {
statusName: 'updated',
statusData: {
isMissingRequiredField: false,
maxSlippage: 2.5,
},
};
render();
mockSendAnalytics.mockClear();
fireEvent.click(screen.getByRole('button', { name: 'Auto' }));
expect(mockSendAnalytics).toHaveBeenCalledWith('swapSlippageChanged', {
previousSlippage: 2.5,
slippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
});
});
it('sends analytics when handling invalid input', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), {
target: { value: 'abc' },
});
expect(mockSendAnalytics).toHaveBeenCalledWith('swapSlippageChanged', {
previousSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
slippage: 0,
});
});
it('sends analytics when handling empty input', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), { target: { value: '' } });
expect(mockSendAnalytics).toHaveBeenCalledWith('swapSlippageChanged', {
previousSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
slippage: 0,
});
});
it('sends analytics when handling decimal input', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), {
target: { value: '2.75' },
});
expect(mockSendAnalytics).toHaveBeenCalledWith('swapSlippageChanged', {
previousSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
slippage: 2.75,
});
});
it('does not send analytics when slippage value does not change', () => {
render();
fireEvent.click(screen.getByRole('button', { name: 'Custom' }));
fireEvent.change(screen.getByRole('textbox'), {
target: { value: FALLBACK_DEFAULT_MAX_SLIPPAGE.toString() },
});
expect(mockSendAnalytics).not.toHaveBeenCalled();
});
});
});