Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,16 @@ const CardAuthentication = () => {
)}
/>
</Box>
{otpError && (
<Box style={styles.errorBox}>
<Text
variant={TextVariant.BodySm}
style={{ color: theme.colors.error.default }}
>
{otpError}
</Text>
</Box>
)}
<Box twClassName="mt-4 items-center">
{resendCountdown > 0 ? (
<Text
Expand All @@ -413,16 +423,6 @@ const CardAuthentication = () => {
)}
</Box>
</Box>
{otpError && (
<Box style={styles.errorBox}>
<Text
variant={TextVariant.BodySm}
style={{ color: theme.colors.error.default }}
>
{otpError}
</Text>
</Box>
)}
</Box>
<Box twClassName="gap-2">
<Button
Expand Down
3 changes: 3 additions & 0 deletions app/components/UI/Card/Views/CardHome/CardHome.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const createStyles = (theme: Theme) =>
defaultHorizontalPadding: {
paddingHorizontal: 16,
},
defaultMarginBottom: {
marginBottom: 16,
},
cardBalanceContainer: {
marginTop: 16,
backgroundColor: theme.colors.background.muted,
Expand Down
Loading
Loading