diff --git a/src/app/Scenes/Onboarding/Auth2/scenes/ForgotPasswordStep.tsx b/src/app/Scenes/Onboarding/Auth2/scenes/ForgotPasswordStep.tsx index bd711f92e1a..b8bf9b37eb2 100644 --- a/src/app/Scenes/Onboarding/Auth2/scenes/ForgotPasswordStep.tsx +++ b/src/app/Scenes/Onboarding/Auth2/scenes/ForgotPasswordStep.tsx @@ -146,7 +146,7 @@ const ForgotPasswordStepForm: React.FC = () => { haptic="impactMedium" testID="returnToLoginButton" > - Return to login + Return to Login diff --git a/src/app/Scenes/Onboarding/Auth2/scenes/LoginOTPStep.tsx b/src/app/Scenes/Onboarding/Auth2/scenes/LoginOTPStep.tsx index 4832a2a8af7..4c9d0fc6093 100644 --- a/src/app/Scenes/Onboarding/Auth2/scenes/LoginOTPStep.tsx +++ b/src/app/Scenes/Onboarding/Auth2/scenes/LoginOTPStep.tsx @@ -15,7 +15,7 @@ import { } from "app/Scenes/Onboarding/Auth2/hooks/useAuthNavigation" import { useInputAutofocus } from "app/Scenes/Onboarding/Auth2/hooks/useInputAutofocus" import { GlobalStore } from "app/store/GlobalStore" -import { Formik, useFormikContext } from "formik" +import { Formik } from "formik" import { useRef, useState } from "react" import * as Yup from "yup" @@ -24,13 +24,22 @@ interface LoginOTPStepFormValues { } export const LoginOTPStep: React.FC = () => { + const [codeType, setCodeType] = useState<"authentication" | "recovery">("authentication") + + const navigation = useAuthNavigation() const screen = useAuthScreen() + const otpRef = useRef(null) + + const { color } = useTheme() + + useInputAutofocus({ + screenName: "LoginOTPStep", + inputRef: otpRef, + }) return ( initialValues={{ otp: "" }} - validateOnChange={true} - validateOnMount={false} validationSchema={Yup.object().shape({ otp: Yup.string().required("This field is required"), })} @@ -54,101 +63,99 @@ export const LoginOTPStep: React.FC = () => { } }} > - - - ) -} - -const LoginOTPStepForm: React.FC = () => { - const [recoveryCodeMode, setRecoveryCodeMode] = useState(false) - - const { - errors, - handleChange, - handleSubmit, - isSubmitting, - isValid, - setErrors, - validateForm, - values, - resetForm, - } = useFormikContext() + {({ + errors, + handleChange, + handleSubmit, + isSubmitting, + isValid, + validateForm, + values, + resetForm, + }) => ( + + { + navigation.goBack() + resetForm() + setCodeType("authentication") + }} + /> + + + + Authentication Code + + { + handleChange("otp")(text) + }} + onBlur={() => validateForm()} + /> + + + + {screen.params?.otpMode === "on_demand" && ( + <> + + + + Your safety and security are important to us. Please check your email for a one-time + authentication code to complete your login. + + + )} - const navigation = useAuthNavigation() - const screen = useAuthScreen() - const otpRef = useRef(null) - - const { color } = useTheme() - - useInputAutofocus({ - screenName: "LoginOTPStep", - inputRef: otpRef, - }) + - const handleBackButtonPress = () => { - resetForm() - navigation.goBack() - setRecoveryCodeMode(false) - } + - return ( - - - - { - // Hide error when the user starts to type again - if (errors.otp) { - setErrors({ otp: undefined }) - validateForm() - } - handleChange("otp")(text) - }} - onBlur={() => validateForm()} - /> - - - - {screen.params?.otpMode === "on_demand" && ( - <> - - Your safety and security are important to us. Please check your email for a one-time - authentication code to complete your login. - - + {screen.params?.otpMode === "standard" && ( + { + if (codeType === "authentication") { + setCodeType("recovery") + } else { + setCodeType("authentication") + } + }} + > + + {codeType === "authentication" + ? "Enter recovery code instead" + : "Enter authentication code"} + + + )} + )} - - - - - - - - { - setRecoveryCodeMode((mode) => !mode) - }} - > - - {recoveryCodeMode ? "Enter authentication code" : "Enter recovery code instead"} - - - + ) } diff --git a/src/app/Scenes/Onboarding/Auth2/scenes/LoginWelcomeStep.tsx b/src/app/Scenes/Onboarding/Auth2/scenes/LoginWelcomeStep.tsx index 2a093ce4023..1798a6d5373 100644 --- a/src/app/Scenes/Onboarding/Auth2/scenes/LoginWelcomeStep.tsx +++ b/src/app/Scenes/Onboarding/Auth2/scenes/LoginWelcomeStep.tsx @@ -128,7 +128,7 @@ const LoginWelcomeStepForm: React.FC = () => { ref={emailRef} spellCheck={false} keyboardType="email-address" - textContentType="none" + textContentType="username" returnKeyType="next" title="Email" value={values.email} diff --git a/src/app/Scenes/Onboarding/Auth2/scenes/__tests__/LoginOTPStep.test.tsx b/src/app/Scenes/Onboarding/Auth2/scenes/__tests__/LoginOTPStep.test.tsx deleted file mode 100644 index 3c49297b976..00000000000 --- a/src/app/Scenes/Onboarding/Auth2/scenes/__tests__/LoginOTPStep.test.tsx +++ /dev/null @@ -1,5 +0,0 @@ -describe("LoginOTPStep", () => { - it("renders correctly", () => { - // TODO - }) -}) diff --git a/src/app/Scenes/Onboarding/Auth2/scenes/__tests__/LoginOTPStep.tests.tsx b/src/app/Scenes/Onboarding/Auth2/scenes/__tests__/LoginOTPStep.tests.tsx new file mode 100644 index 00000000000..6af50e62e27 --- /dev/null +++ b/src/app/Scenes/Onboarding/Auth2/scenes/__tests__/LoginOTPStep.tests.tsx @@ -0,0 +1,46 @@ +import { fireEvent, screen } from "@testing-library/react-native" +import { LoginOTPStep } from "app/Scenes/Onboarding/Auth2/scenes/LoginOTPStep" +import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" + +const mockUseAuthScreen = jest.fn() + +jest.mock("app/Scenes/Onboarding/Auth2/hooks/useAuthNavigation", () => ({ + useAuthNavigation: jest.fn(), + useAuthScreen: () => mockUseAuthScreen(), +})) + +describe("LoginOTPStep", () => { + beforeEach(() => { + mockUseAuthScreen.mockReturnValue({ params: { otpMode: "standard" } }) + }) + + it("renders correctly", () => { + renderWithWrappers() + + expect(screen.getByText("Authentication Code")).toBeDefined() + expect(screen.getByText("Authentication code")).toBeDefined() + }) + + it("allows the user to switch between authentication and recovery codes", () => { + renderWithWrappers() + + const recoveryCodeButton = screen.getByText("Enter recovery code instead") + expect(recoveryCodeButton).toBeDefined() + + fireEvent.press(recoveryCodeButton) + + expect(screen.getByText("Recovery code")).toBeDefined() + }) + + it("renders instructions when OTP mode is 'on_demand'", () => { + mockUseAuthScreen.mockReturnValue({ params: { otpMode: "on_demand" } }) + + renderWithWrappers() + + expect( + screen.getByText( + "Your safety and security are important to us. Please check your email for a one-time authentication code to complete your login." + ) + ).toBeDefined() + }) +})