VAN-299/fix_error_message_preference (#125)

This commit is contained in:
Zainab Amir
2021-02-06 16:44:17 +05:00
committed by GitHub
parent d1b84bde48
commit 80f4cf00a4
3 changed files with 74 additions and 161 deletions

View File

@@ -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) => {
<>
<div id="main" className="d-flex justify-content-center m-4">
<div className="d-flex flex-column mw-500">
{(emptyFieldError || props.status) && (
{bannerErrorMessage ? (
<Alert id="validation-errors" variant="danger">
<Alert.Heading>{intl.formatMessage(messages['forgot.password.empty.new.password.error.heading'])}</Alert.Heading>
<ul><li>{emptyFieldError || props.errors}</li></ul>
<ul><li>{bannerErrorMessage}</li></ul>
</Alert>
)}
) : null}
<Form>
<h3 className="mt-3">
{intl.formatMessage(messages['reset.password.page.heading'])}
@@ -102,8 +117,8 @@ const ResetPasswordPage = (props) => {
</p>
<ValidationFormGroup
for="reset-password-input"
invalid={!passwordValid || emptyFieldError !== ''}
invalidMessage={validationMessage || emptyFieldError}
invalid={!passwordValid}
invalidMessage={validationMessage}
className="w-100"
>
<Form.Label htmlFor="reset-password-input" className="h6 mr-1">
@@ -114,7 +129,6 @@ const ResetPasswordPage = (props) => {
id="reset-password-input"
type="password"
placeholder=""
onChange={() => setEmptyFieldError('')}
onBlur={e => handleNewPasswordChange(e)}
/>
</ValidationFormGroup>

View File

@@ -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 => (
<IntlProvider locale="en">
<Provider store={store}>{children}</Provider>
@@ -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(<IntlResetPasswordPage {...props} />))
.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(<IntlResetPasswordPage {...props} />));
// 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(<IntlResetPasswordPage {...props} />));
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(<IntlResetPasswordPage {...props} />));
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', () => {

View File

@@ -47,20 +47,6 @@ exports[`ResetPasswordPage should match pending reset message section snapshot 1
<div
className="d-flex flex-column mw-500"
>
<div
className="fade alert alert-danger show"
id="validation-errors"
role="alert"
>
<div
className="alert-heading h4"
>
We couldn't reset your password.
</div>
<ul>
<li />
</ul>
</div>
<form
className=""
>
@@ -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
</div>
`;
exports[`ResetPasswordPage should match unsuccessful reset message section snapshot 1`] = `
<div
className="d-flex justify-content-center m-4"
id="main"
>
<div
className="d-flex flex-column mw-500"
>
<div
className="fade alert alert-danger show"
id="validation-errors"
role="alert"
>
<div
className="alert-heading h4"
>
We couldn't reset your password.
</div>
<ul>
<li>
Password reset was unsuccessful.
</li>
</ul>
</div>
<form
className=""
>
<h3
className="mt-3"
>
Reset your password
</h3>
<p
className="mb-4"
>
Enter and confirm your new password.
</p>
<div
className="form-group w-100"
>
<label
className="h6 mr-1 form-label"
htmlFor="reset-password-input"
>
New Password
</label>
<input
aria-describedby=""
className="form-control"
id="reset-password-input"
name="new-password1"
onBlur={[Function]}
onChange={[Function]}
placeholder=""
type="password"
/>
</div>
<div
className="form-group w-100"
>
<label
className="h6 mr-1 form-label"
htmlFor="confirm-password-input"
>
Confirm Password
</label>
<input
aria-describedby=""
className="form-control"
id="confirm-password-input"
name="new-password2"
onChange={[Function]}
placeholder=""
type="password"
value=""
/>
<strong
className="invalid-feedback"
id="confirm-password-input-invalid-feedback"
>
Passwords do not match.
</strong>
</div>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-failure btn-primary btn btn-primary"
disabled={false}
onClick={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
Reset my password
</span>
</button>
</form>
</div>
</div>
`;
exports[`ResetPasswordPage show spinner component during token validation 1`] = `
<div
className="container position-absolute h-90"