Forgot password page redesigned

This commit is contained in:
adeelehsan
2021-05-06 02:52:14 +05:00
committed by Waheed Ahmed
parent 70a372eb5b
commit df38c9e599
12 changed files with 229 additions and 585 deletions

3
.env
View File

@@ -16,7 +16,8 @@ SITE_NAME=null
USER_INFO_COOKIE_NAME=null
AUTHN_MINIMAL_HEADER=true
LOGIN_ISSUE_SUPPORT_LINK=null
REGISTRATION_OPTIONAL_FIELDS=null
REGISTRATION_OPTIONAL_FIELDS=''
USER_SURVEY_COOKIE_NAME=null
COOKIE_DOMAIN=null
WELCOME_PAGE_SUPPORT_LINK=null
INFO_EMAIL=''

View File

@@ -27,3 +27,4 @@ REGISTRATION_OPTIONAL_FIELDS=''
USER_SURVEY_COOKIE_NAME='openedx-user-survey-type'
COOKIE_DOMAIN='localhost'
WELCOME_PAGE_SUPPORT_LINK='http://localhost:1999/welcome'
INFO_EMAIL='info@edx.org'

View File

@@ -343,6 +343,11 @@ select.form-control {
}
}
#forgotpassword-success-alert {
.alert-link {
color: #454545 !important;
}
}
@media (min-width: 1024px) {
.mw-500 {

View File

@@ -1,52 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Alert } from '@edx/paragon';
import messages from './messages';
const ConfirmationAlert = (props) => {
const { email, intl } = props;
return (
<Alert id="confirmation-alert" variant="success">
<Alert.Heading>{intl.formatMessage(messages['forgot.password.confirmation.title'])}</Alert.Heading>
<p>
<FormattedMessage
id="forgot.password.confirmation.message"
defaultMessage="You entered {strongEmail}. If this email address is associated with your
{platformName} account, we will send a message with password recovery instructions to this email address."
description="Forgot password confirmation message"
values={{
strongEmail: <strong className="data-hj-suppress">{email}</strong>,
platformName: getConfig().SITE_NAME,
}}
/>
</p>
<p>{intl.formatMessage(messages['forgot.password.confirmation.info'])}</p>
<p>
<FormattedMessage
id="forgot.password.technical.support.help.message"
defaultMessage="If you need further assistance, {technicalSupportLink}."
description="Message to help user contact technical support"
values={{
technicalSupportLink: (
<Alert.Link href={getConfig().PASSWORD_RESET_SUPPORT_LINK}>
{intl.formatMessage(messages['forgot.password.confirmation.support.link'])}
</Alert.Link>
),
}}
/>
</p>
</Alert>
);
};
ConfirmationAlert.propTypes = {
email: PropTypes.string.isRequired,
intl: intlShape.isRequired,
};
export default injectIntl(ConfirmationAlert);

View File

@@ -1,34 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import { mergeConfig } from '@edx/frontend-platform';
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
import ConfirmationAlert from '../ConfirmationAlert';
const IntlConfirmationAlertMessage = injectIntl(ConfirmationAlert);
describe('ConfirmationAlert', () => {
const supportLink = 'https://support.test.com/What-if-I-did-not-receive-a-password-reset-message';
mergeConfig({
PASSWORD_RESET_SUPPORT_LINK: supportLink,
});
it('should match default confirmation message', () => {
const confirmationAlertMessage = mount(
<IntlProvider locale="en">
<IntlConfirmationAlertMessage email="test@example.com" />
</IntlProvider>,
);
const expectedMessage = 'Check your email'
+ 'You entered test@example.com. If this email address is associated with your edX account, '
+ 'we will send a message with password recovery instructions to this email address.'
+ 'If you do not receive a password reset message after 1 minute, verify that you entered '
+ 'the correct email address, or check your spam folder.'
+ 'If you need further assistance, contact technical support.';
expect(confirmationAlertMessage.find('#confirmation-alert').first().text()).toEqual(expectedMessage);
expect(confirmationAlertMessage.find('#confirmation-alert').find('a').props().href).toEqual(supportLink);
});
});

View File

@@ -4,7 +4,7 @@ import { Formik } from 'formik';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { Redirect } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { getConfig } from '@edx/frontend-platform';
import { sendPageEvent } from '@edx/frontend-platform/analytics';
@@ -13,37 +13,46 @@ import {
Alert,
Form,
StatefulButton,
Hyperlink,
Icon,
} from '@edx/paragon';
import { Info } from '@edx/paragon/icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
import { faSpinner, faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import { forgotPassword } from './data/actions';
import { forgotPasswordResultSelector } from './data/selectors';
import RequestInProgressAlert from './RequestInProgressAlert';
import SuccessAlert from './SuccessAlert';
import messages from './messages';
import {
AuthnValidationFormGroup,
} from '../common-components';
import { FormGroup } from '../common-components';
import APIFailureMessage from '../common-components/APIFailureMessage';
import { INTERNAL_SERVER_ERROR, LOGIN_PAGE, VALID_EMAIL_REGEX } from '../data/constants';
import LoginHelpLinks from '../login/LoginHelpLinks';
import { updatePathWithQueryParams, windowScrollTo } from '../data/utils';
const ForgotPasswordPage = (props) => {
const { intl, status } = props;
const { intl } = props;
let { status } = props;
const platformName = getConfig().SITE_NAME;
const regex = new RegExp(VALID_EMAIL_REGEX, 'i');
const [validationError, setValidationError] = useState('');
const getErrorMessage = (errors) => {
const header = intl.formatMessage(messages['forgot.password.request.server.error']);
const renderAlertMessages = (errors, email) => {
const header = intl.formatMessage(messages['forgot.password.error.alert.title']);
if (status === 'complete') {
status = 'default';
return (
<SuccessAlert email={email} />
);
}
if (errors.email) {
return (
<Alert variant="danger">
<Icon src={Info} className="alert-icon" />
<Alert.Heading>{header}</Alert.Heading>
<ul><li>{errors.email}</li></ul>
<p>{`${errors.email}${intl.formatMessage(messages['extend.field.errors'])}`}</p>
</Alert>
);
}
@@ -69,74 +78,84 @@ const ForgotPasswordPage = (props) => {
sendPageEvent('login_and_registration', 'reset');
return (
<Formik
initialValues={{ email: '' }}
validateOnChange={false}
validate={(values) => {
const validationMessage = getValidationMessage(values.email);
<div>
<span className="nav nav-tabs">
<Link className="nav-item nav-link" to={updatePathWithQueryParams(LOGIN_PAGE)}>
<FontAwesomeIcon className="mr-2" icon={faChevronLeft} /> {intl.formatMessage(messages['sign.in.text'])}
</Link>
</span>
<div id="main-content" className="main-content">
<Formik
initialValues={{ email: '' }}
validateOnChange={false}
validate={(values) => {
const validationMessage = getValidationMessage(values.email);
if (validationMessage !== '') {
windowScrollTo({ left: 0, top: 0, behavior: 'smooth' });
return { email: validationMessage };
}
if (validationMessage !== '') {
windowScrollTo({ left: 0, top: 0, behavior: 'smooth' });
return { email: validationMessage };
}
return {};
}}
onSubmit={(values) => { props.forgotPassword(values.email); }}
>
{({
errors, handleSubmit, setFieldValue, values,
}) => (
<>
<Helmet>
<title>{intl.formatMessage(messages['forgot.password.page.title'],
{ siteName: getConfig().SITE_NAME })}
</title>
</Helmet>
{status === 'complete' ? <Redirect to={updatePathWithQueryParams(LOGIN_PAGE)} /> : null}
<div className="d-flex justify-content-center m-4">
<div className="d-flex flex-column">
<Form className="mw-500">
{ getErrorMessage(errors) }
<h1 className="mt-3 h3">
{intl.formatMessage(messages['forgot.password.page.heading'])}
</h1>
<p className="mb-4">
{intl.formatMessage(messages['forgot.password.page.instructions'])}
</p>
<AuthnValidationFormGroup
label={intl.formatMessage(messages['forgot.password.page.email.field.label'])}
for="forgot-password-input"
name="email"
type="email"
invalid={validationError !== ''}
ariaInvalid={validationError !== ''}
invalidMessage={validationError}
value={values.email}
onBlur={() => getValidationMessage(values.email)}
onChange={e => setFieldValue('email', e.target.value)}
helpText={intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })}
className="mb-0 w-100"
inputFieldStyle="border-gray-600"
/>
<LoginHelpLinks page="forgot-password" />
<StatefulButton
type="submit"
className="btn-primary mt-3"
state={status}
labels={{
default: intl.formatMessage(messages['forgot.password.page.submit.button']),
}}
icons={{ pending: <FontAwesomeIcon icon={faSpinner} spin /> }}
onClick={handleSubmit}
onMouseDown={(e) => e.preventDefault()}
/>
</Form>
</div>
</div>
</>
)}
</Formik>
return {};
}}
onSubmit={(values) => { props.forgotPassword(values.email); }}
>
{({
errors, handleSubmit, setFieldValue, values,
}) => (
<>
<Helmet>
<title>{intl.formatMessage(messages['forgot.password.page.title'],
{ siteName: getConfig().SITE_NAME })}
</title>
</Helmet>
<div className="d-flex justify-content-center">
<div className="d-flex flex-column">
<Form className="mw-xs">
{ renderAlertMessages(errors, values.email) }
<h3>
{intl.formatMessage(messages['forgot.password.page.heading'])}
</h3>
<p className="mb-4">
{intl.formatMessage(messages['forgot.password.page.instructions'])}
</p>
<FormGroup
floatingLabel={intl.formatMessage(messages['forgot.password.page.email.field.label'])}
name="email"
errorMessage={validationError}
value={values.email}
handleBlur={() => getValidationMessage(values.email)}
handleChange={e => setFieldValue('email', e.target.value)}
helpText={[intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })]}
/>
<StatefulButton
type="submit"
variant="brand"
className="login-button-width"
state={status}
labels={{
default: intl.formatMessage(messages['forgot.password.page.submit.button']),
}}
icons={{ pending: <FontAwesomeIcon icon={faSpinner} spin /> }}
onClick={handleSubmit}
onMouseDown={(e) => e.preventDefault()}
/>
<Hyperlink id="forgot-password" className="btn btn-link font-weight-500 text-body" destination={getConfig().LOGIN_ISSUE_SUPPORT_LINK}>
{intl.formatMessage(messages['need.help.sign.in.text'])}
</Hyperlink>
<p className="mt-5">{intl.formatMessage(
messages['additional.help.text'],
{ infoEmail: process.env.INFO_EMAIL },
)}
</p>
</Form>
</div>
</div>
</>
)}
</Formik>
</div>
</div>
);
};

View File

@@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Alert, Icon } from '@edx/paragon';
import { CheckCircle } from '@edx/paragon/icons';
import messages from './messages';
const SuccessAlert = (props) => {
const { email, intl } = props;
return (
<Alert id="forgotpassword-success-alert" variant="success">
<Icon src={CheckCircle} className="alert-icon" />
<Alert.Heading>{intl.formatMessage(messages['confirmation.message.title'])}</Alert.Heading>
<p>
<FormattedMessage
id="forgot.password.confirmation.message"
defaultMessage="We sent an email to {email} with instructions to reset your password.
If you do not receive a password reset message after 1 minute, verify that you entered
the correct email address, or check your spam folder. If you need further assistance, {supportLink}."
description="Forgot password confirmation message"
values={{
email: <span className="data-hj-suppress">{email}</span>,
supportLink: (
<Alert.Link className="alert-link" href={getConfig().PASSWORD_RESET_SUPPORT_LINK}>
{intl.formatMessage(messages['confirmation.support.link'])}
</Alert.Link>
),
}}
/>
</p>
</Alert>
);
};
SuccessAlert.propTypes = {
email: PropTypes.string.isRequired,
intl: intlShape.isRequired,
};
export default injectIntl(SuccessAlert);

View File

@@ -8,19 +8,20 @@ const messages = defineMessages({
},
'forgot.password.page.heading': {
id: 'forgot.password.page.heading',
defaultMessage: 'Password assistance',
defaultMessage: 'Reset password',
description: 'The page heading for the forgot password page.',
},
'forgot.password.page.instructions': {
id: 'forgot.password.page.instructions',
defaultMessage: 'Please enter your log-in or recovery email address below and we will send you an email with instructions.',
defaultMessage: 'Please enter your email address below and we will send you an email with instructions on how to reset your password.',
description: 'Instructions message for forgot password page.',
},
'forgot.password.page.invalid.email.message': {
id: 'forgot.password.page.invalid.email.message',
defaultMessage: "The email address you've provided isn't formatted correctly.",
description: 'Invalid email address message for the forgot password page.',
defaultMessage: 'Enter a valid email address',
description: 'Invalid email address message for input field.',
},
'forgot.password.page.email.field.label': {
id: 'forgot.password.page.email.field.label',
defaultMessage: 'Email',
@@ -28,12 +29,12 @@ const messages = defineMessages({
},
'forgot.password.page.submit.button': {
id: 'forgot.password.page.submit.button',
defaultMessage: 'Recover my password',
defaultMessage: 'Submit',
description: 'Submit button text for the forgot password page.',
},
'forgot.password.request.server.error': {
id: 'forgot.password.request.server.error',
defaultMessage: 'We couldnt send the password recovery email.',
'forgot.password.error.alert.title': {
id: 'forgot.password.error.alert.title.',
defaultMessage: 'We were unable to contact you.',
description: 'Failed to send password recovery email.',
},
'forgot.password.error.message.title': {
@@ -48,7 +49,7 @@ const messages = defineMessages({
},
'forgot.password.empty.email.field.error': {
id: 'forgot.password.empty.email.field.error',
defaultMessage: 'Please enter your email.',
defaultMessage: 'Enter your email',
description: 'Error message that appears when user tries to submit empty email field',
},
'forgot.password.invalid.email.heading': {
@@ -66,5 +67,38 @@ const messages = defineMessages({
defaultMessage: 'The email address you used to register with {platformName}',
description: 'text help for the email',
},
// Confirmation Alert Message
'confirmation.message.title': {
id: 'confirmation.message.title',
defaultMessage: 'Check your email',
description: 'Forgot password confirmation message title',
},
'confirmation.support.link': {
id: 'confirmation.support.link',
defaultMessage: 'contact technical support',
description: 'Technical support link text',
},
'need.help.sign.in.text': {
id: 'need.help.sign.in.text',
defaultMessage: 'Need help signing in?',
description: 'Sign in help link on forgot password page',
},
'additional.help.text': {
id: 'additional.help.text',
defaultMessage: 'For additional help, contact edX support at {infoEmail}',
description: 'additional help text on forgot password page',
},
'sign.in.text': {
id: 'sign.in.text',
defaultMessage: 'Sign In',
description: 'login page link on password page',
},
'extend.field.errors': {
id: 'extend.field.errors',
defaultMessage: ' below.',
description: 'extends the field error for alert message',
},
});
export default messages;

View File

@@ -1,11 +1,10 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import { MemoryRouter } from 'react-router-dom';
import renderer from 'react-test-renderer';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { createMemoryHistory } from 'history';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
import CookiePolicyBanner from '@edx/frontend-component-cookie-policy-banner';
import * as analytics from '@edx/frontend-platform/analytics';
@@ -19,7 +18,11 @@ analytics.sendPageEvent = jest.fn();
const IntlForgotPasswordPage = injectIntl(ForgotPasswordPage);
const mockStore = configureStore();
const history = createMemoryHistory();
const initialState = {
forgotPassword: {
status: null,
},
};
describe('ForgotPasswordPage', () => {
let props = {};
@@ -27,12 +30,14 @@ describe('ForgotPasswordPage', () => {
const reduxWrapper = children => (
<IntlProvider locale="en">
<Provider store={store}>{children}</Provider>
<MemoryRouter>
<Provider store={store}>{children}</Provider>
</MemoryRouter>
</IntlProvider>
);
beforeEach(() => {
store = mockStore();
store = mockStore(initialState);
props = {
forgotPassword: jest.fn(),
status: null,
@@ -65,45 +70,29 @@ describe('ForgotPasswordPage', () => {
expect(tree).toMatchSnapshot();
});
it('should match success section snapshot', () => {
props = {
...props,
status: 'complete',
};
renderer.create(
reduxWrapper(
<Router history={history}>
<IntlForgotPasswordPage {...props} />
</Router>,
),
);
expect(history.location.pathname).toEqual('/login');
});
it('should display need other help signing in button', () => {
const wrapper = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
expect(wrapper.find('button.field-link').first().text()).toEqual('Need other help signing in?');
expect(wrapper.find('#forgot-password.btn-link').first().text()).toEqual('Need help signing in?');
});
it('should display email validation error message', async () => {
const validationMessage = "We couldnt send the password recovery email.The email address you've provided isn't formatted correctly.";
const validationMessage = 'We were unable to contact you.Enter a valid email address below.';
const wrapper = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
wrapper.find('input#forgot-password-input').simulate(
wrapper.find('input#email').simulate(
'change', { target: { value: 'invalid-email', name: 'email' } },
);
await act(async () => { await wrapper.find('button.btn-primary').simulate('click'); });
await act(async () => { await wrapper.find('button.btn-brand').simulate('click'); });
wrapper.update();
expect(wrapper.find('.alert-danger').text()).toEqual(validationMessage);
});
it('should show alert on server error', () => {
props = {
...props,
status: INTERNAL_SERVER_ERROR,
};
const expectedMessage = 'We couldnt send the password recovery email.'
store = mockStore({
forgotPassword: { status: INTERNAL_SERVER_ERROR },
});
const expectedMessage = 'We were unable to contact you.'
+ 'An error has occurred. Try refreshing the page, or check your internet connection.';
const wrapper = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
@@ -111,10 +100,10 @@ describe('ForgotPasswordPage', () => {
});
it('should display empty email validation message', async () => {
const validationMessage = 'We couldnt send the password recovery email.Please enter your email.';
const validationMessage = 'We were unable to contact you.Enter your email below.';
const forgotPasswordPage = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
await act(async () => { await forgotPasswordPage.find('button.btn-primary').simulate('click'); });
await act(async () => { await forgotPasswordPage.find('button.btn-brand').simulate('click'); });
forgotPasswordPage.update();
expect(forgotPasswordPage.find('.alert-danger').text()).toEqual(validationMessage);
@@ -133,7 +122,7 @@ describe('ForgotPasswordPage', () => {
it('should not display any error message on change event', () => {
const forgotPasswordPage = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
const emailInput = forgotPasswordPage.find('input#forgot-password-input');
const emailInput = forgotPasswordPage.find('input#email');
emailInput.simulate('change', { target: { value: 'invalid-email', name: 'email' } });
forgotPasswordPage.update();
@@ -141,20 +130,35 @@ describe('ForgotPasswordPage', () => {
});
it('should display error message on blur event', async () => {
const validationMessage = 'Please enter your email.';
const validationMessage = 'Enter your email';
const forgotPasswordPage = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
const emailInput = forgotPasswordPage.find('input#forgot-password-input');
const emailInput = forgotPasswordPage.find('input#email');
await act(async () => {
await emailInput.simulate('blur', { target: { value: '', name: 'email' } });
});
forgotPasswordPage.update();
expect(forgotPasswordPage.find('#forgot-password-input-invalid-feedback').text()).toEqual(validationMessage);
expect(forgotPasswordPage.find('.pgn__form-control-description-invalid').text()).toEqual(validationMessage);
});
it('check cookie rendered', () => {
const forgotPage = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
expect(forgotPage.find(<CookiePolicyBanner />)).toBeTruthy();
});
it('should display success message after email is sent', async () => {
store = mockStore({
...initialState,
forgotPassword: {
status: 'complete',
},
});
const successMessage = 'Check your emailWe sent an email to with instructions to reset your password. If you do not '
+ 'receive a password reset message after 1 minute, verify that you entered the correct email address,'
+ ' or check your spam folder. If you need further assistance, contact technical support.';
const wrapper = mount(reduxWrapper(<IntlForgotPasswordPage {...props} />));
expect(wrapper.find('.alert-success').text()).toEqual(successMessage);
});
});

View File

@@ -1,358 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ForgotPasswordPage should match default section snapshot 1`] = `
<div
className="d-flex justify-content-center m-4"
>
<div
className="d-flex flex-column"
>
<form
className="mw-500"
>
<h1
className="mt-3 h3"
>
Password assistance
</h1>
<p
className="mb-4"
>
Please enter your log-in or recovery email address below and we will send you an email with instructions.
</p>
<div
className="form-group mb-0 w-100"
>
<label
className="pgn__form-label pt-10 focus-out"
>
Email
</label>
<input
aria-describedby=""
aria-invalid={false}
autoComplete="on"
className="form-control border-gray-600"
id="forgot-password-input"
name="email"
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
required={true}
type="email"
value=""
/>
<span />
</div>
<button
className="mt-2 field-link small"
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-caret-right fa-w-6 mr-1"
data-icon="caret-right"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 192 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"
fill="currentColor"
style={Object {}}
/>
</svg>
Need other help signing in?
</button>
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
/>
</div>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-null btn-primary mt-3 btn btn-primary"
disabled={false}
onClick={[Function]}
onMouseDown={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span>
Recover my password
</span>
</span>
</button>
</form>
</div>
</div>
`;
exports[`ForgotPasswordPage should match forbidden section snapshot 1`] = `
<div
className="d-flex justify-content-center m-4"
>
<div
className="d-flex flex-column"
>
<form
className="mw-500"
>
<div
className="fade alert alert-danger show"
role="alert"
>
<div
className="alert-heading h4"
>
An error occurred.
</div>
<ul>
<li>
Your previous request is in progress, please try again in a few moments.
</li>
</ul>
</div>
<h1
className="mt-3 h3"
>
Password assistance
</h1>
<p
className="mb-4"
>
Please enter your log-in or recovery email address below and we will send you an email with instructions.
</p>
<div
className="form-group mb-0 w-100"
>
<label
className="pgn__form-label pt-10 focus-out"
>
Email
</label>
<input
aria-describedby=""
aria-invalid={false}
autoComplete="on"
className="form-control border-gray-600"
id="forgot-password-input"
name="email"
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
required={true}
type="email"
value=""
/>
<span />
</div>
<button
className="mt-2 field-link small"
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-caret-right fa-w-6 mr-1"
data-icon="caret-right"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 192 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"
fill="currentColor"
style={Object {}}
/>
</svg>
Need other help signing in?
</button>
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
/>
</div>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-forbidden btn-primary mt-3 btn btn-primary"
disabled={false}
onClick={[Function]}
onMouseDown={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span>
Recover my password
</span>
</span>
</button>
</form>
</div>
</div>
`;
exports[`ForgotPasswordPage should match pending section snapshot 1`] = `
<div
className="d-flex justify-content-center m-4"
>
<div
className="d-flex flex-column"
>
<form
className="mw-500"
>
<h1
className="mt-3 h3"
>
Password assistance
</h1>
<p
className="mb-4"
>
Please enter your log-in or recovery email address below and we will send you an email with instructions.
</p>
<div
className="form-group mb-0 w-100"
>
<label
className="pgn__form-label pt-10 focus-out"
>
Email
</label>
<input
aria-describedby=""
aria-invalid={false}
autoComplete="on"
className="form-control border-gray-600"
id="forgot-password-input"
name="email"
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
required={true}
type="email"
value=""
/>
<span />
</div>
<button
className="mt-2 field-link small"
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-caret-right fa-w-6 mr-1"
data-icon="caret-right"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 192 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"
fill="currentColor"
style={Object {}}
/>
</svg>
Need other help signing in?
</button>
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
/>
</div>
<button
aria-disabled={true}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn-primary mt-3 disabled btn btn-primary"
disabled={false}
onClick={[Function]}
onMouseDown={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span
className="pgn__stateful-btn-icon"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-spinner fa-w-16 fa-spin "
data-icon="spinner"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z"
fill="currentColor"
style={Object {}}
/>
</svg>
</span>
<span>
Recover my password
</span>
</span>
</button>
</form>
</div>
</div>
`;

View File

@@ -27,7 +27,6 @@ import {
RedirectLogistration, SocialAuthProviders, ThirdPartyAuthAlert, RenderInstitutionButton,
InstitutionLogistration, FormGroup, PasswordField,
} from '../common-components';
import ConfirmationAlert from '../common-components/ConfirmationAlert';
import { getThirdPartyAuthContext } from '../common-components/data/actions';
import { thirdPartyAuthContextSelector } from '../common-components/data/selectors';
import EnterpriseSSO from '../common-components/EnterpriseSSO';
@@ -197,9 +196,6 @@ class LoginPage extends React.Component {
{this.props.loginError ? <LoginFailureMessage loginError={this.props.loginError} /> : null}
{submitState === DEFAULT_STATE && this.state.isSubmitted ? windowScrollTo({ left: 0, top: 0, behavior: 'smooth' }) : null}
{activationMsgType && <AccountActivationMessage messageType={activationMsgType} />}
{this.props.forgotPassword.status === 'complete' && !this.props.loginError ? (
<ConfirmationAlert email={this.props.forgotPassword.email} />
) : null}
<Form className="test">
<FormGroup
name="email"

View File

@@ -259,22 +259,6 @@ describe('LoginPage', () => {
expect(loginPage.find('#tpa-alert').find('p').text()).toEqual(expectedMessage);
});
it('should match forget password confirmation message', () => {
store = mockStore({
...initialState,
forgotPassword: { status: 'complete', email: 'test@example.com' },
});
const confirmationMessage = 'Check your email'
+ 'You entered test@example.com. If this email address is associated with your edX account, '
+ 'we will send a message with password recovery instructions to this email address.If you do not '
+ 'receive a password reset message after 1 minute, verify that you entered the correct email address, '
+ 'or check your spam folder.If you need further assistance, contact technical support.';
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
expect(loginPage.find('#confirmation-alert').first().text()).toEqual(confirmationMessage);
});
it('should match invalid login form error message', () => {
const errorMessage = 'Please fill in the fields below.';
store = mockStore({