diff --git a/src/reset-password/ResetPasswordPage.jsx b/src/reset-password/ResetPasswordPage.jsx index e50c26d2..deafd312 100644 --- a/src/reset-password/ResetPasswordPage.jsx +++ b/src/reset-password/ResetPasswordPage.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { @@ -26,7 +26,15 @@ const ResetPasswordPage = (props) => { const [passwordValid, setPasswordValidValue] = useState(true); const [passwordMatch, setPasswordMatchValue] = useState(true); const [validationMessage, setvalidationMessage] = useState(''); - const [emptyFieldError, setEmptyFieldError] = useState(''); + const [bannerErrorMessage, setbannerErrorMessage] = useState(''); + + useEffect(() => { + if (props.errors) { + setbannerErrorMessage(props.errors); + setvalidationMessage(props.errors); + setPasswordValidValue(false); + } + }, [props.status === 'failure']); const validatePasswordFromBackend = async (newPassword) => { let errorMessage; @@ -42,7 +50,13 @@ const ResetPasswordPage = (props) => { const handleNewPasswordChange = (e) => { const newPassword = e.target.value; setNewPasswordValue(newPassword); - validatePasswordFromBackend(newPassword); + + if (newPassword === '') { + setPasswordValidValue(false); + setvalidationMessage(intl.formatMessage(messages['reset.password.empty.new.password.field.error'])); + } else { + validatePasswordFromBackend(newPassword); + } }; const handleConfirmPasswordChange = (e) => { @@ -53,23 +67,24 @@ const ResetPasswordPage = (props) => { const handleSubmit = (e) => { e.preventDefault(); - window.scrollTo({ left: 0, top: 0, behavior: 'smooth' }); + window.scrollTo({ left: 0, top: 0, behavior: 'smooth' }); if (newPasswordInput === '') { - setEmptyFieldError(intl.formatMessage(messages['reset.password.empty.new.password.field.error'])); + setPasswordValidValue(false); + setvalidationMessage(intl.formatMessage(messages['reset.password.empty.new.password.field.error'])); + setbannerErrorMessage(intl.formatMessage(messages['reset.password.empty.new.password.field.error'])); return; } if (newPasswordInput !== confirmPasswordInput) { setPasswordMatchValue(false); return; } - if (passwordValid && passwordMatch) { - const formPayload = { - new_password1: newPasswordInput, - new_password2: confirmPasswordInput, - }; - props.resetPassword(formPayload, props.token, params); - } + + const formPayload = { + new_password1: newPasswordInput, + new_password2: confirmPasswordInput, + }; + props.resetPassword(formPayload, props.token, params); }; if (props.token_status === 'pending') { @@ -87,12 +102,12 @@ const ResetPasswordPage = (props) => { <>
- {(emptyFieldError || props.status) && ( + {bannerErrorMessage ? ( {intl.formatMessage(messages['forgot.password.empty.new.password.error.heading'])} -
  • {emptyFieldError || props.errors}
+
  • {bannerErrorMessage}
- )} + ) : null}

{intl.formatMessage(messages['reset.password.page.heading'])} @@ -102,8 +117,8 @@ const ResetPasswordPage = (props) => {

@@ -114,7 +129,6 @@ const ResetPasswordPage = (props) => { id="reset-password-input" type="password" placeholder="" - onChange={() => setEmptyFieldError('')} onBlur={e => handleNewPasswordChange(e)} /> diff --git a/src/reset-password/tests/ResetPasswordPage.test.jsx b/src/reset-password/tests/ResetPasswordPage.test.jsx index a6a77bd9..deee3e75 100644 --- a/src/reset-password/tests/ResetPasswordPage.test.jsx +++ b/src/reset-password/tests/ResetPasswordPage.test.jsx @@ -21,6 +21,9 @@ describe('ResetPasswordPage', () => { let props = {}; let store = {}; + const emptyFieldError = 'Please enter your New Password.'; + const validationMessage = 'This password is too short. It must contain at least 8 characters. This password must contain at least 1 number.'; + const reduxWrapper = children => ( {children} @@ -114,25 +117,7 @@ describe('ResetPasswordPage', () => { expect(tree).toMatchSnapshot(); }); - it('should match unsuccessful reset message section snapshot', () => { - props = { - ...props, - token_status: 'valid', - status: 'failure', - errors: 'Password reset was unsuccessful.', - }; - const tree = renderer.create(reduxWrapper()) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); - it('should display invalid password message', async () => { - const validationMessage = 'This password is too short. It must contain at least 8 characters. This password must contain at least 1 number.'; - const data = { - validation_decisions: { - password: validationMessage, - }, - }; props = { ...props, token_status: 'valid', @@ -140,19 +125,47 @@ describe('ResetPasswordPage', () => { auth.getHttpClient = jest.fn(() => ({ post: async () => ({ - data, + data: { + validation_decisions: { + password: validationMessage, + }, + }, catch: () => {}, }), })); const resetPasswordPage = mount(reduxWrapper()); + + // Focus out of empty field await act(async () => { await resetPasswordPage.find('input#reset-password-input').simulate('blur'); }); resetPasswordPage.update(); + expect(resetPasswordPage.find('#reset-password-input-invalid-feedback').text()).toEqual(emptyFieldError); + + // Enter non-compliant password + await act(async () => { + await resetPasswordPage.find('input#reset-password-input').simulate('blur', { target: { value: 'invalid' } }); + }); expect(resetPasswordPage.find('#reset-password-input-invalid-feedback').text()).toEqual(validationMessage); }); + it('should display error message on empty form submission', () => { + const bannerMessage = 'We couldn\'t reset your password.'.concat(emptyFieldError); + props = { + ...props, + token_status: 'valid', + token: 'token', + }; + + const resetPasswordPage = mount(reduxWrapper()); + resetPasswordPage.find('button.btn-primary').simulate('click'); + + resetPasswordPage.update(); + expect(resetPasswordPage.find('#reset-password-input-invalid-feedback').text()).toEqual(emptyFieldError); + expect(resetPasswordPage.find('#validation-errors').first().text()).toEqual(bannerMessage); + }); + it('with valid inputs resetPassword action is dispatch', async () => { const newPassword = 'test-password1'; props = { @@ -204,20 +217,24 @@ describe('ResetPasswordPage', () => { resetPasswordPage.unmount(); }); - it('should display empty new password field error', () => { - const validationMessage = 'Please enter your New Password.'; + it('should not update the banner message on focus out', async () => { + const bannerMessage = 'We couldn\'t reset your password.'.concat(validationMessage); props = { ...props, token_status: 'valid', token: 'token', + errors: validationMessage, }; const resetPasswordPage = mount(reduxWrapper()); - resetPasswordPage.find('button.btn-primary').simulate('click'); + expect(resetPasswordPage.find('#validation-errors').first().text()).toEqual(bannerMessage); - resetPasswordPage.update(); - expect(resetPasswordPage.find('#reset-password-input-invalid-feedback').text()).toEqual(validationMessage); - expect(resetPasswordPage.find('#validation-errors').first().text()).toEqual('We couldn\'t reset your password.'.concat(validationMessage)); + await act(async () => { + await resetPasswordPage.find('input#reset-password-input').simulate('blur', { target: { value: '' } }); + }); + // On blur event, the banner message remains same + expect(resetPasswordPage.find('#reset-password-input-invalid-feedback').text()).toEqual(emptyFieldError); + expect(resetPasswordPage.find('#validation-errors').first().text()).toEqual(bannerMessage); }); it('check cookie rendered', () => { diff --git a/src/reset-password/tests/__snapshots__/ResetPasswordPage.test.jsx.snap b/src/reset-password/tests/__snapshots__/ResetPasswordPage.test.jsx.snap index 269e30b7..ea58ef24 100644 --- a/src/reset-password/tests/__snapshots__/ResetPasswordPage.test.jsx.snap +++ b/src/reset-password/tests/__snapshots__/ResetPasswordPage.test.jsx.snap @@ -47,20 +47,6 @@ exports[`ResetPasswordPage should match pending reset message section snapshot 1
- @@ -89,7 +75,6 @@ exports[`ResetPasswordPage should match pending reset message section snapshot 1 id="reset-password-input" name="new-password1" onBlur={[Function]} - onChange={[Function]} placeholder="" type="password" /> @@ -196,7 +181,6 @@ exports[`ResetPasswordPage should match reset password default section snapshot id="reset-password-input" name="new-password1" onBlur={[Function]} - onChange={[Function]} placeholder="" type="password" /> @@ -284,108 +268,6 @@ exports[`ResetPasswordPage should match successful reset message section snapsho
`; -exports[`ResetPasswordPage should match unsuccessful reset message section snapshot 1`] = ` -
-
- - -

- Reset your password -

-

- Enter and confirm your new password. -

-
- - -
-
- - - - Passwords do not match. - -
- - -
-
-`; - exports[`ResetPasswordPage show spinner component during token validation 1`] = `