diff --git a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.test.tsx b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.test.tsx index 74f3a3418c5e..398acddd68d6 100644 --- a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.test.tsx +++ b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.test.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen, waitFor } from '@testing-library/react-native'; +import { TextInput } from 'react-native'; import { renderScreen } from '../../../../../util/test/renderWithProvider'; import CardAuthentication from './CardAuthentication'; import Routes from '../../../../../constants/navigation/Routes'; @@ -973,6 +974,50 @@ describe('CardAuthentication Component', () => { }); }); + it('displays error below OTP input fields', async () => { + mockLogin.mockResolvedValue({ + isOtpRequired: true, + userId: 'user-123', + phoneNumber: '+1 (555) 123-****', + }); + + render(); + const emailInput = screen.getByPlaceholderText( + 'Enter your email address', + ); + const passwordInput = screen.getByPlaceholderText('Enter your password'); + const loginButton = screen.getByTestId( + CardAuthenticationSelectors.VERIFY_ACCOUNT_BUTTON, + ); + + fireEvent.changeText(emailInput, 'test@example.com'); + fireEvent.changeText(passwordInput, 'password123'); + fireEvent.press(loginButton); + + await waitFor(() => { + expect( + screen.getByText('Enter your verification code'), + ).toBeOnTheScreen(); + }); + + mockUseCardProviderAuthentication.mockReturnValue({ + login: mockLogin, + loading: false, + error: null, + clearError: mockClearError, + sendOtpLogin: mockSendOtpLogin, + otpLoading: false, + otpError: 'The code you entered is incorrect', + clearOtpError: mockClearOtpError, + }); + + await waitFor(() => { + const errorText = screen.getByText('The code you entered is incorrect'); + expect(errorText).toBeOnTheScreen(); + expect(screen.getByText('Verification Code')).toBeOnTheScreen(); + }); + }); + it('does not display OTP error box when no error exists in OTP step', async () => { mockLogin.mockResolvedValue({ isOtpRequired: true, @@ -1002,6 +1047,66 @@ describe('CardAuthentication Component', () => { screen.queryByText('Invalid verification code'), ).not.toBeOnTheScreen(); }); + + it('calls clearOtpError when user types in OTP field', async () => { + mockLogin.mockResolvedValue({ + isOtpRequired: true, + userId: 'user-123', + }); + + render(); + const emailInput = screen.getByPlaceholderText( + 'Enter your email address', + ); + const passwordInput = screen.getByPlaceholderText('Enter your password'); + const loginButton = screen.getByTestId( + CardAuthenticationSelectors.VERIFY_ACCOUNT_BUTTON, + ); + + fireEvent.changeText(emailInput, 'test@example.com'); + fireEvent.changeText(passwordInput, 'password123'); + fireEvent.press(loginButton); + + await waitFor(() => { + expect( + screen.getByText('Enter your verification code'), + ).toBeOnTheScreen(); + }); + + // Update mock to include OTP error after entering OTP step + mockUseCardProviderAuthentication.mockReturnValue({ + login: mockLogin, + loading: false, + error: null, + clearError: mockClearError, + sendOtpLogin: mockSendOtpLogin, + otpLoading: false, + otpError: 'Invalid verification code', + clearOtpError: mockClearOtpError, + }); + + // Wait for error to appear + await waitFor(() => { + expect(screen.getByText('Invalid verification code')).toBeOnTheScreen(); + }); + + // Clear the mock to track new calls + mockClearOtpError.mockClear(); + + // Find the OTP input (CodeField renders as TextInput with number-pad keyboard) + const allInputs = screen.UNSAFE_queryAllByType(TextInput); + const otpInput = allInputs.find( + (input) => input.props.keyboardType === 'number-pad', + ); + + expect(otpInput).toBeDefined(); + + if (otpInput) { + fireEvent.changeText(otpInput, '1'); + + expect(mockClearOtpError).toHaveBeenCalled(); + } + }); }); describe('OTP Step - Loading States', () => { diff --git a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx index 1cda96795538..c56c605c9344 100644 --- a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx +++ b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx @@ -388,6 +388,16 @@ const CardAuthentication = () => { )} /> + {otpError && ( + + + {otpError} + + + )} {resendCountdown > 0 ? ( { )} - {otpError && ( - - - {otpError} - - - )}