diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 3422715e0..0e78b2abb 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -11,6 +11,7 @@ import Consent from './pages/Consent'; import ErrorPage from './pages/ErrorPage'; import ForgotPassword from './pages/ForgotPassword'; import Passcode from './pages/Passcode'; +import PasswordRegisterWithUsername from './pages/PasswordRegisterWithUsername'; import Register from './pages/Register'; import ResetPassword from './pages/ResetPassword'; import SecondaryRegister from './pages/SecondaryRegister'; @@ -68,8 +69,10 @@ const App = () => { {/* register */} } /> - {/* TODO: @simeng LOG-4456 */} - + } + /> } /> {/* forgot password */} diff --git a/packages/ui/src/containers/ResetPassword/index.module.scss b/packages/ui/src/containers/SetPassword/index.module.scss similarity index 100% rename from packages/ui/src/containers/ResetPassword/index.module.scss rename to packages/ui/src/containers/SetPassword/index.module.scss diff --git a/packages/ui/src/containers/ResetPassword/index.test.tsx b/packages/ui/src/containers/SetPassword/index.test.tsx similarity index 69% rename from packages/ui/src/containers/ResetPassword/index.test.tsx rename to packages/ui/src/containers/SetPassword/index.test.tsx index f8425ce63..65090342e 100644 --- a/packages/ui/src/containers/ResetPassword/index.test.tsx +++ b/packages/ui/src/containers/SetPassword/index.test.tsx @@ -1,40 +1,39 @@ -import { fireEvent, act, waitFor } from '@testing-library/react'; +import { render, fireEvent, act, waitFor } from '@testing-library/react'; -import renderWithPageContext from '@/__mocks__/RenderWithPageContext'; -import { resetPassword } from '@/apis/forgot-password'; +import SetPassword from '.'; -import ResetPassword from '.'; +describe('', () => { + const submit = jest.fn(); + const clearError = jest.fn(); -const mockedNavigate = jest.fn(); + afterEach(() => { + jest.clearAllMocks(); + }); -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useNavigate: () => mockedNavigate, -})); - -jest.mock('@/apis/forgot-password', () => ({ - resetPassword: jest.fn(async () => ({ redirectTo: '/' })), -})); - -describe('', () => { - test('default render', () => { - const { queryByText, container } = renderWithPageContext(); + test('default render ', () => { + const { queryByText, container } = render( + + ); expect(container.querySelector('input[name="new-password"]')).not.toBeNull(); expect(container.querySelector('input[name="confirm-new-password"]')).not.toBeNull(); + expect(queryByText('error')).not.toBeNull(); expect(queryByText('action.save_password')).not.toBeNull(); }); test('password are required', () => { - const { queryByText, getByText } = renderWithPageContext(); + const { queryByText, getByText } = render( + + ); const submitButton = getByText('action.save_password'); fireEvent.click(submitButton); + expect(clearError).toBeCalled(); expect(queryByText('password_required')).not.toBeNull(); - expect(resetPassword).not.toBeCalled(); + expect(submit).not.toBeCalled(); }); test('password less than 6 chars should throw', () => { - const { queryByText, getByText, container } = renderWithPageContext(); + const { queryByText, getByText, container } = render(); const submitButton = getByText('action.save_password'); const passwordInput = container.querySelector('input[name="new-password"]'); @@ -48,7 +47,7 @@ describe('', () => { expect(queryByText('password_min_length')).not.toBeNull(); - expect(resetPassword).not.toBeCalled(); + expect(submit).not.toBeCalled(); act(() => { // Clear error @@ -61,7 +60,7 @@ describe('', () => { }); test('password mismatch with confirmPassword should throw', () => { - const { queryByText, getByText, container } = renderWithPageContext(); + const { queryByText, getByText, container } = render(); const submitButton = getByText('action.save_password'); const passwordInput = container.querySelector('input[name="new-password"]'); const confirmPasswordInput = container.querySelector('input[name="confirm-new-password"]'); @@ -80,7 +79,7 @@ describe('', () => { expect(queryByText('passwords_do_not_match')).not.toBeNull(); - expect(resetPassword).not.toBeCalled(); + expect(submit).not.toBeCalled(); act(() => { // Clear Error @@ -93,7 +92,7 @@ describe('', () => { }); test('should submit properly', async () => { - const { queryByText, getByText, container } = renderWithPageContext(); + const { queryByText, getByText, container } = render(); const submitButton = getByText('action.save_password'); const passwordInput = container.querySelector('input[name="new-password"]'); const confirmPasswordInput = container.querySelector('input[name="confirm-new-password"]'); @@ -113,7 +112,7 @@ describe('', () => { expect(queryByText('passwords_do_not_match')).toBeNull(); await waitFor(() => { - expect(resetPassword).toBeCalled(); + expect(submit).toBeCalledWith('123456'); }); }); }); diff --git a/packages/ui/src/containers/ResetPassword/index.tsx b/packages/ui/src/containers/SetPassword/index.tsx similarity index 51% rename from packages/ui/src/containers/ResetPassword/index.tsx rename to packages/ui/src/containers/SetPassword/index.tsx index 096ba680b..a51603dc6 100644 --- a/packages/ui/src/containers/ResetPassword/index.tsx +++ b/packages/ui/src/containers/SetPassword/index.tsx @@ -1,17 +1,11 @@ import classNames from 'classnames'; -import { useEffect, useCallback, useMemo, useContext } from 'react'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; -import { resetPassword } from '@/apis/forgot-password'; import Button from '@/components/Button'; import ErrorMessage from '@/components/ErrorMessage'; import Input from '@/components/Input'; -import type { ErrorHandlers } from '@/hooks/use-api'; -import useApi from '@/hooks/use-api'; -import { useConfirmModal } from '@/hooks/use-confirm-modal'; import useForm from '@/hooks/use-form'; -import { PageContext } from '@/hooks/use-page-context'; import { passwordValidation, confirmPasswordValidation } from '@/utils/field-validations'; import * as styles from './index.module.scss'; @@ -20,6 +14,9 @@ type Props = { className?: string; // eslint-disable-next-line react/boolean-prop-naming autoFocus?: boolean; + onSubmit: (password: string) => void; + errorMessage?: string; + clearErrorMessage?: () => void; }; type FieldState = { @@ -32,61 +29,31 @@ const defaultState: FieldState = { confirmPassword: '', }; -const ResetPassword = ({ className, autoFocus }: Props) => { +const SetPassword = ({ + className, + autoFocus, + onSubmit, + errorMessage, + clearErrorMessage, +}: Props) => { const { t } = useTranslation(); - const { setToast } = useContext(PageContext); - const { - fieldValue, - formErrorMessage, - setFieldValue, - register, - validateForm, - setFormErrorMessage, - } = useForm(defaultState); - const { show } = useConfirmModal(); - const navigate = useNavigate(); - - const resetPasswordErrorHandlers: ErrorHandlers = useMemo( - () => ({ - 'session.verification_session_not_found': async (error) => { - await show({ type: 'alert', ModalContent: error.message, cancelText: 'action.got_it' }); - navigate(-1); - }, - 'session.verification_expired': async (error) => { - await show({ type: 'alert', ModalContent: error.message, cancelText: 'action.got_it' }); - navigate(-1); - }, - 'user.same_password': (error) => { - setFormErrorMessage(error.message); - }, - }), - [navigate, setFormErrorMessage, show] - ); - - const { result, run: asyncRegister } = useApi(resetPassword, resetPasswordErrorHandlers); + const { fieldValue, setFieldValue, register, validateForm } = useForm(defaultState); const onSubmitHandler = useCallback( async (event?: React.FormEvent) => { event?.preventDefault(); - setFormErrorMessage(undefined); + clearErrorMessage?.(); if (!validateForm()) { return; } - void asyncRegister(fieldValue.password); + onSubmit(fieldValue.password); }, - [setFormErrorMessage, validateForm, asyncRegister, fieldValue.password] + [clearErrorMessage, validateForm, onSubmit, fieldValue.password] ); - useEffect(() => { - if (result) { - setToast(t('description.password_changed')); - navigate('/sign-in', { replace: true }); - } - }, [navigate, result, setToast, t]); - return (
{ setFieldValue((state) => ({ ...state, confirmPassword: '' })); }} /> - {formErrorMessage && ( - {formErrorMessage} - )} + {errorMessage && {errorMessage}}