fix(auth): retain input on failure + copy fixes
This commit is contained in:
@@ -100,12 +100,14 @@ const ForgotPassword = () => {
|
||||
await signIn('password', formData).then(() => {
|
||||
setEmail(values.email);
|
||||
setFlow('reset-verification');
|
||||
// Only clear the form once the request succeeded; a failed request
|
||||
// keeps the typed email so the user can retry without re-entering it.
|
||||
forgotPasswordForm.reset();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error resetting password: ', error);
|
||||
toast.error('Error resetting password.');
|
||||
} finally {
|
||||
forgotPasswordForm.reset();
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
@@ -121,12 +123,14 @@ const ForgotPassword = () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await signIn('password', formData);
|
||||
// Only clear on success (navigation follows); a failed submit keeps the
|
||||
// entered password so the user can correct and retry.
|
||||
resetVerificationForm.reset();
|
||||
router.push('/');
|
||||
} catch (error) {
|
||||
console.error('Error resetting password: ', error);
|
||||
toast.error('Error resetting password.');
|
||||
} finally {
|
||||
resetVerificationForm.reset();
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
@@ -230,7 +234,7 @@ const ForgotPassword = () => {
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Please enter the one-time password sent to your
|
||||
phone.
|
||||
email.
|
||||
</FormDescription>
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
@@ -265,7 +269,7 @@ const ForgotPassword = () => {
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className='text-xl'>
|
||||
Confirm Passsword
|
||||
Confirm Password
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
|
||||
@@ -132,12 +132,14 @@ const SignIn = () => {
|
||||
formData.append('flow', flow);
|
||||
setLoading(true);
|
||||
try {
|
||||
await signIn('password', formData).then(() => router.push('/dashboard'));
|
||||
await signIn('password', formData).then(() => {
|
||||
signInForm.reset();
|
||||
router.push('/dashboard');
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error signing in:', error);
|
||||
toast.error('Error signing in.');
|
||||
} finally {
|
||||
signInForm.reset();
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
@@ -153,6 +155,7 @@ const SignIn = () => {
|
||||
if (values.confirmPassword !== values.password)
|
||||
throw new ConvexError('Passwords do not match.');
|
||||
await signIn('password', formData).then(() => {
|
||||
signUpForm.reset();
|
||||
setEmail(values.email);
|
||||
setFlow('email-verification');
|
||||
});
|
||||
@@ -160,7 +163,6 @@ const SignIn = () => {
|
||||
console.error('Error signing up:', error);
|
||||
toast.error('Error signing up.');
|
||||
} finally {
|
||||
signUpForm.reset();
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
@@ -174,12 +176,14 @@ const SignIn = () => {
|
||||
formData.append('email', email);
|
||||
setLoading(true);
|
||||
try {
|
||||
await signIn('password', formData).then(() => router.push('/dashboard'));
|
||||
await signIn('password', formData).then(() => {
|
||||
verifyEmailForm.reset();
|
||||
router.push('/dashboard');
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error verifying email:', error);
|
||||
toast.error('Error verifying email.');
|
||||
} finally {
|
||||
verifyEmailForm.reset();
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
@@ -232,7 +236,7 @@ const SignIn = () => {
|
||||
/>
|
||||
<SubmitButton
|
||||
disabled={loading}
|
||||
pendingText='Signing Up...'
|
||||
pendingText='Verifying...'
|
||||
className='mx-auto w-2/3 text-xl font-semibold'
|
||||
>
|
||||
Verify Email
|
||||
@@ -423,7 +427,7 @@ const SignIn = () => {
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className='text-xl'>
|
||||
Confirm Passsword
|
||||
Confirm Password
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import SignIn from '../../src/app/(auth)/sign-in/page';
|
||||
|
||||
const { mockSignIn, mockToastError } = vi.hoisted(() => ({
|
||||
mockSignIn: vi.fn(),
|
||||
mockToastError: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@convex-dev/auth/react', () => ({
|
||||
useAuthActions: () => ({ signIn: mockSignIn }),
|
||||
}));
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
|
||||
}));
|
||||
|
||||
vi.mock('sonner', () => ({
|
||||
toast: {
|
||||
error: mockToastError,
|
||||
success: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('sign-in input retention', () => {
|
||||
it('keeps the typed email and password after a failed sign-in', async () => {
|
||||
mockSignIn.mockRejectedValue(new Error('Invalid credentials'));
|
||||
|
||||
render(<SignIn />);
|
||||
|
||||
const emailInput = screen.getByPlaceholderText('you@example.com');
|
||||
const passwordInput = screen.getByPlaceholderText('Your password');
|
||||
|
||||
fireEvent.change(emailInput, {
|
||||
target: { value: 'person@example.com' },
|
||||
});
|
||||
fireEvent.change(passwordInput, { target: { value: 'Sup3rSecret!' } });
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Sign In' }));
|
||||
|
||||
await waitFor(() => expect(mockToastError).toHaveBeenCalled());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(emailInput).toHaveValue('person@example.com');
|
||||
expect(passwordInput).toHaveValue('Sup3rSecret!');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user