Merge pull request #79 from edx/van-281/handle-500-on-register-page

Handle 500 api response
This commit is contained in:
Uzair Rasheed
2021-01-29 19:09:39 +05:00
committed by GitHub
6 changed files with 106 additions and 44 deletions

View File

@@ -1,47 +1,55 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Alert } from '@edx/paragon';
const hasNoErrors = (userErrors) => (
userErrors.every((errorList) => (!errorList[0]))
);
import { INTERNAL_SERVER_ERROR } from '../login/data/constants';
import messages from './messages';
const RegistrationFailureMessage = (props) => {
const errorMessage = props.errors;
const { errorCode } = props.errors;
const userErrors = [];
useEffect(() => {
window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
}, [props.submitCount]);
Object.keys(errorMessage).forEach((key) => {
const errors = errorMessage[key];
const errorList = errors.map((error) => (
(error.user_message) ? (
<li key={error} className="text-left">
{error.user_message}
let serverError;
switch (errorCode) {
case INTERNAL_SERVER_ERROR:
serverError = (
<li key={INTERNAL_SERVER_ERROR} className="text-left">
{props.intl.formatMessage(messages['registration.request.server.error'])}
</li>
) : null
));
userErrors.push(errorList);
});
);
userErrors.push(serverError);
break;
default:
Object.keys(errorMessage).forEach((key) => {
const errors = errorMessage[key];
const errorList = errors.map((error) => (
(error.user_message) ? (
<li key={error} className="text-left">
{error.user_message}
</li>
) : null
));
userErrors.push(errorList);
});
}
return (
hasNoErrors(userErrors) ? null : (
!userErrors.length ? null : (
<Alert variant="danger">
<Alert.Heading>
<FormattedMessage
id="registration.request.failure.header.description.message"
defaultMessage="We couldn't create your account."
description="error message when registration failure."
/>
{props.intl.formatMessage(messages['registration.request.failure.header'])}
</Alert.Heading>
<div>
<ul>
{userErrors}
</ul>
</div>
<ul>
{userErrors}
</ul>
</Alert>
)
);
@@ -56,8 +64,10 @@ RegistrationFailureMessage.propTypes = {
errors: PropTypes.shape({
email: PropTypes.array,
username: PropTypes.array,
errorCode: PropTypes.string,
}),
submitCount: PropTypes.number,
intl: intlShape.isRequired,
};
export default RegistrationFailureMessage;
export default injectIntl(RegistrationFailureMessage);

View File

@@ -24,6 +24,7 @@ import {
getRegistrationForm,
registerRequest,
} from './service';
import { INTERNAL_SERVER_ERROR } from '../../login/data/constants';
export function* handleNewUserRegistration(action) {
try {
@@ -39,6 +40,8 @@ export function* handleNewUserRegistration(action) {
const statusCodes = [400, 409, 403];
if (e.response && statusCodes.includes(e.response.status)) {
yield put(registerNewUserFailure(e.response.data));
} else {
yield put(registerNewUserFailure({ errorCode: INTERNAL_SERVER_ERROR }));
}
logError(e);
}

View File

@@ -1,5 +1,6 @@
import { runSaga } from 'redux-saga';
import { camelCaseObject } from '@edx/frontend-platform';
import * as actions from '../actions';
import {
fetchRealtimeValidations,
@@ -188,6 +189,32 @@ describe('handleNewUserRegistration', () => {
registerRequest.mockClear();
});
it('should handle 500 error code', async () => {
const registerErrorResponse = {
response: {
status: 500,
data: {
errorCode: 'internal-server-error',
},
},
};
const registerRequest = jest.spyOn(api, 'registerRequest').mockImplementation(() => Promise.reject(registerErrorResponse));
const dispatched = [];
await runSaga(
{ dispatch: (action) => dispatched.push(action) },
handleNewUserRegistration,
params,
);
expect(dispatched).toEqual([
actions.registerNewUserBegin(),
actions.registerNewUserFailure(camelCaseObject(registerErrorResponse.response.data)),
]);
registerRequest.mockClear();
});
it('should call service and dispatch error action', async () => {
const loginErrorResponse = {
response: {

View File

@@ -116,6 +116,16 @@ const messages = defineMessages({
defaultMessage: '(optional)',
description: 'Text that appears with optional field labels',
},
'registration.request.server.error': {
id: 'registration.request.server.error',
defaultMessage: 'An error has occurred. Try refreshing the page, or check your Internet connection.',
description: 'error message on server error.',
},
'registration.request.failure.header': {
id: 'registration.request.failure.header',
defaultMessage: 'We couldn\'t create your account.',
description: 'error message when registration failure.',
},
});
export default messages;

View File

@@ -9,7 +9,9 @@ import * as analytics from '@edx/frontend-platform/analytics';
import RegistrationPage from '../RegistrationPage';
import { RenderInstitutionButton } from '../../common-components';
import RegistrationFailureMessage from '../RegistrationFailure';
import { PENDING_STATE } from '../../data/constants';
import { INTERNAL_SERVER_ERROR } from '../../login/data/constants';
import { fetchRegistrationForm, fetchRealtimeValidations, registerNewUser } from '../data/actions';
jest.mock('@edx/frontend-platform/analytics');
@@ -18,6 +20,7 @@ analytics.sendTrackEvent = jest.fn();
analytics.sendPageEvent = jest.fn();
const IntlRegistrationPage = injectIntl(RegistrationPage);
const IntlRegistrationFailure = injectIntl(RegistrationFailureMessage);
const mockStore = configureStore();
describe('./RegistrationPage.js', () => {
@@ -326,6 +329,19 @@ describe('./RegistrationPage.js', () => {
expect(tree.toJSON()).toMatchSnapshot();
});
it('should match internal server error message', () => {
props = {
errors: {
errorCode: INTERNAL_SERVER_ERROR,
},
};
const registrationPage = mount(reduxWrapper(<IntlRegistrationFailure {...props} />));
expect(registrationPage.find('div.alert-heading').length).toEqual(1);
const expectedMessage = 'We couldn\'t create your account.An error has occurred. Try refreshing the page, or check your Internet connection.';
expect(registrationPage.find('div.alert').first().text()).toEqual(expectedMessage);
});
it('should match pending button state snapshot', () => {
store = mockStore({
...initialState,

View File

@@ -976,24 +976,20 @@ exports[`./RegistrationPage.js should show error message on 409 1`] = `
<div
className="alert-heading h4"
>
<span>
We couldn't create your account.
</span>
</div>
<div>
<ul>
<li
className="text-left"
>
It looks like test@gmail.com belongs to an existing account. Try again with a different email address.
</li>
<li
className="text-left"
>
It looks like test belongs to an existing account. Try again with a different username.
</li>
</ul>
We couldn't create your account.
</div>
<ul>
<li
className="text-left"
>
It looks like test@gmail.com belongs to an existing account. Try again with a different email address.
</li>
<li
className="text-left"
>
It looks like test belongs to an existing account. Try again with a different username.
</li>
</ul>
</div>
<div
className="text-left"