diff --git a/src/app/(auth-routes)/forgot-password/page.test.tsx b/src/app/(auth-routes)/forgot-password/page.test.tsx new file mode 100644 index 000000000..024edbf5d --- /dev/null +++ b/src/app/(auth-routes)/forgot-password/page.test.tsx @@ -0,0 +1,116 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; + +import ForgotPassword from "./page"; + +vi.mock("next/link", () => ({ + default: ({ children }: { children: React.ReactNode }) => children, +})); + +describe("forgot password page", () => { + it("renders the initial email input form", () => { + expect.hasAssertions(); + render(); + + expect(screen.getByText(/forgot password/i)).toBeInTheDocument(); + + expect(screen.getByText(/enter the email address/i)).toBeInTheDocument(); + + expect( + screen.getByPlaceholderText(/enter your email/i), + ).toBeInTheDocument(); + + expect(screen.getByText(/send/i)).toBeInTheDocument(); + }); + + it("shows email error for unregistered email", async () => { + expect.hasAssertions(); + + render(); + + const emailInput = screen.getByPlaceholderText(/enter your email/i); + const sendButton = screen.getByText(/send/i); + + fireEvent.change(emailInput, { + target: { value: "unregistered@example.com" }, + }); + fireEvent.click(sendButton); + + await waitFor(() => { + expect( + screen.getByText(/this email doesn't match our records/i), + ).toBeInTheDocument(); + }); + }); + + it("proceeds to verification code stage on valid email", async () => { + expect.hasAssertions(); + + render(); + + const emailInput = screen.getByPlaceholderText(/enter your email/i); + const sendButton = screen.getByText(/send/i); + + fireEvent.change(emailInput, { + target: { value: "akinsanyaadeyinka4166@gmail.com" }, + }); + fireEvent.click(sendButton); + + await waitFor(() => { + expect(screen.getByText("Verification Code")).toBeInTheDocument(); + }); + }); + + it("shows error for incorrect OTP", async () => { + expect.hasAssertions(); + + render(); + + const emailInput = screen.getByPlaceholderText(/enter your email/i); + const sendButton = screen.getByText(/send/i); + + fireEvent.change(emailInput, { + target: { value: "akinsanyaadeyinka4166@gmail.com" }, + }); + fireEvent.click(sendButton); + + await waitFor(() => { + expect(screen.getByText("Verification Code")).toBeInTheDocument(); + }); + + const otpInput = screen.getByTestId("forgot-password-otp-input"); + fireEvent.change(otpInput, { target: { value: "000000" } }); + + await waitFor(() => { + expect( + screen.getByText(/the otp entered is not correct/i), + ).toBeInTheDocument(); + }); + }); + + it("proceeds to reset password stage on correct OTP", async () => { + expect.hasAssertions(); + + render(); + + const emailInput = screen.getByPlaceholderText(/enter your email/i); + const sendButton = screen.getByText(/send/i); + + fireEvent.change(emailInput, { + target: { value: "akinsanyaadeyinka4166@gmail.com" }, + }); + fireEvent.click(sendButton); + + await waitFor(() => { + expect(screen.getByText("Verification Code")).toBeInTheDocument(); + }); + + const otpInput = screen.getByTestId("forgot-password-otp-input"); + fireEvent.change(otpInput, { target: { value: "123456" } }); + const verifyButton = screen.getByText(/verify/i); + fireEvent.click(verifyButton); + + await waitFor(() => { + expect(screen.getByText(/verification successful/i)).toBeInTheDocument(); + }); + }); +}); diff --git a/src/app/(auth-routes)/forgot-password/page.tsx b/src/app/(auth-routes)/forgot-password/page.tsx index 7361f9940..b48ca531b 100644 --- a/src/app/(auth-routes)/forgot-password/page.tsx +++ b/src/app/(auth-routes)/forgot-password/page.tsx @@ -173,6 +173,7 @@ const ForgotPassword = () => { { setCode(value); setIsCodeComplete(value.length === 6); @@ -282,7 +283,9 @@ const ForgotPassword = () => { { element: Default, stage: 0, - onSubmit: () => setCurrentStage(1), + onSubmit: () => { + if (!emailError) setCurrentStage(1); + }, }, { element: VerificationCode, diff --git a/src/components/common/input-otp/index.tsx b/src/components/common/input-otp/index.tsx index ea8a93fed..87717ceb7 100644 --- a/src/components/common/input-otp/index.tsx +++ b/src/components/common/input-otp/index.tsx @@ -13,15 +13,22 @@ interface Properties { onComplete?: (value: string) => void; } -export function InputOtp({ - maxLength = 6, - slotClassName, - className, - onChange, - onComplete, -}: Properties) { +export function InputOtp(properties: Properties) { + const { + maxLength = 6, + slotClassName, + className, + onChange, + onComplete, + ...rest + } = properties; return ( - + {/* eslint-disable unicorn/no-useless-spread */} {/* eslint-disable unicorn/no-new-array */}