Login: Add error handling for 500 status code

This handles the cases where some internal server error occurs and
no error message is shown to users.

VAN-95
This commit is contained in:
Zainab Amir
2020-12-31 17:01:49 +05:00
committed by GitHub
parent b92439c2b3
commit f2957d4885
6 changed files with 92 additions and 11 deletions

View File

@@ -5,7 +5,7 @@ import { Alert, Hyperlink } from '@edx/paragon';
import PropTypes from 'prop-types';
import { processLink } from '../data/utils/dataUtils';
import { INACTIVE_USER, NON_COMPLIANT_PASSWORD_EXCEPTION } from './data/constants';
import { INACTIVE_USER, INTERNAL_SERVER_ERROR, NON_COMPLIANT_PASSWORD_EXCEPTION } from './data/constants';
import messages from './messages';
const LoginFailureMessage = (props) => {
@@ -60,6 +60,17 @@ const LoginFailureMessage = (props) => {
);
break;
}
case INTERNAL_SERVER_ERROR:
errorList = (
<li key={INTERNAL_SERVER_ERROR}>
<FormattedMessage
id="login.internal.server.error.message"
defaultMessage="An error has occurred. Try refreshing the page, or check your Internet connection."
description="Error message that appears when server responds with 500 error code"
/>
</li>
);
break;
default:
// TODO: use errorCode instead of processing error messages on frontend
errorList = value.trim().split('\n');

View File

@@ -1,2 +1,3 @@
export const NON_COMPLIANT_PASSWORD_EXCEPTION = 'NonCompliantPasswordException';
export const INACTIVE_USER = 'inactive-user';
export const INTERNAL_SERVER_ERROR = 'internal-server-error';
export const NON_COMPLIANT_PASSWORD_EXCEPTION = 'NonCompliantPasswordException';

View File

@@ -2,6 +2,7 @@ import { call, put, takeEvery } from 'redux-saga/effects';
import { camelCaseObject } from '@edx/frontend-platform';
import { logError } from '@edx/frontend-platform/logging';
import { INTERNAL_SERVER_ERROR } from './constants';
// Actions
import {
@@ -67,8 +68,12 @@ export function* handleLoginRequest(action) {
));
} catch (e) {
const statusCodes = [400];
if (e.response && statusCodes.includes(e.response.status)) {
yield put(loginRequestFailure(camelCaseObject(e.response.data)));
if (e.response) {
if (statusCodes.includes(e.response.status)) {
yield put(loginRequestFailure(camelCaseObject(e.response.data)));
} else {
yield put(loginRequestFailure(camelCaseObject({ errorCode: INTERNAL_SERVER_ERROR })));
}
}
logError(e);
}

View File

@@ -235,7 +235,7 @@ describe('handleLoginRequest', () => {
});
it('should call service and dispatch error action', async () => {
const loginErrorRespinse = {
const loginErrorResponse = {
response: {
status: 400,
data: {
@@ -244,7 +244,7 @@ describe('handleLoginRequest', () => {
},
};
const loginRequest = jest.spyOn(api, 'loginRequest')
.mockImplementation(() => Promise.reject(loginErrorRespinse));
.mockImplementation(() => Promise.reject(loginErrorResponse));
const dispatched = [];
await runSaga(
@@ -257,7 +257,33 @@ describe('handleLoginRequest', () => {
expect(loggingService.logError).toHaveBeenCalled();
expect(dispatched).toEqual([
actions.loginRequestBegin(),
actions.loginRequestFailure(camelCaseObject(loginErrorRespinse.response.data)),
actions.loginRequestFailure(camelCaseObject(loginErrorResponse.response.data)),
]);
loginRequest.mockClear();
});
it('should handle 500 error code', async () => {
const loginErrorResponse = {
response: {
status: 500,
data: {
errorCode: 'internal-server-error',
},
},
};
const loginRequest = jest.spyOn(api, 'loginRequest').mockImplementation(() => Promise.reject(loginErrorResponse));
const dispatched = [];
await runSaga(
{ dispatch: (action) => dispatched.push(action) },
handleLoginRequest,
params,
);
expect(dispatched).toEqual([
actions.loginRequestBegin(),
actions.loginRequestFailure(camelCaseObject(loginErrorResponse.response.data)),
]);
loginRequest.mockClear();
});
@@ -302,7 +328,7 @@ describe('handleNewUserRegistration', () => {
});
it('should call service and dispatch error action', async () => {
const loginErrorRespinse = {
const loginErrorResponse = {
response: {
status: 400,
data: {
@@ -311,7 +337,7 @@ describe('handleNewUserRegistration', () => {
},
};
const registerRequest = jest.spyOn(api, 'registerRequest')
.mockImplementation(() => Promise.reject(loginErrorRespinse));
.mockImplementation(() => Promise.reject(loginErrorResponse));
const dispatched = [];
await runSaga(
@@ -324,7 +350,7 @@ describe('handleNewUserRegistration', () => {
expect(loggingService.logError).toHaveBeenCalled();
expect(dispatched).toEqual([
actions.registerNewUserBegin(),
actions.registerNewUserFailure(loginErrorRespinse.response.data),
actions.registerNewUserFailure(loginErrorResponse.response.data),
]);
registerRequest.mockClear();
});

View File

@@ -3,7 +3,7 @@ import renderer from 'react-test-renderer';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import LoginFailureMessage from '../LoginFailure';
import { INACTIVE_USER, NON_COMPLIANT_PASSWORD_EXCEPTION } from '../data/constants';
import { INACTIVE_USER, INTERNAL_SERVER_ERROR, NON_COMPLIANT_PASSWORD_EXCEPTION } from '../data/constants';
describe('LoginFailureMessage', () => {
let props = {};
@@ -45,6 +45,22 @@ describe('LoginFailureMessage', () => {
expect(tree).toMatchSnapshot();
});
it('should match internal server error message snapshot', () => {
props = {
loginError: {
errorCode: INTERNAL_SERVER_ERROR,
},
};
const tree = renderer.create(
<IntlProvider locale="en">
<LoginFailureMessage {...props} />
</IntlProvider>,
).toJSON();
expect(tree).toMatchSnapshot();
});
it('should match direct render of error message snapshot', () => {
props = {
loginError: {

View File

@@ -89,6 +89,28 @@ exports[`LoginFailureMessage should match inactive user error message snapshot 1
</div>
`;
exports[`LoginFailureMessage should match internal server error message snapshot 1`] = `
<div
className="fade alert alert-danger show"
role="alert"
>
<div
className="alert-heading h4"
>
<span>
We couldn't sign you in.
</span>
</div>
<ul>
<li>
<span>
An error has occurred. Try refreshing the page, or check your Internet connection.
</span>
</li>
</ul>
</div>
`;
exports[`LoginFailureMessage should match non compliant password error message snapshot 1`] = `
<div
className="fade alert alert-danger show"