Merge branch 'alerts-test' into 'develop'

Add tests for alerts action

See merge request soapbox-pub/soapbox-fe!1464
This commit is contained in:
Justin 2022-05-27 19:33:30 +00:00
commit 3082064453
4 changed files with 224 additions and 69 deletions

View file

@ -0,0 +1,149 @@
import { AxiosError } from 'axios';
import { mockStore } from 'soapbox/jest/test-helpers';
import rootReducer from 'soapbox/reducers';
import { dismissAlert, showAlert, showAlertForError } from '../alerts';
const buildError = (message: string, status: number) => new AxiosError<any>(message, String(status), null, null, {
data: {
error: message,
},
statusText: String(status),
status,
headers: {},
config: {},
});
let store;
beforeEach(() => {
const state = rootReducer(undefined, {});
store = mockStore(state);
});
describe('dismissAlert()', () => {
it('dispatches the proper actions', async() => {
const alert = 'hello world';
const expectedActions = [
{ type: 'ALERT_DISMISS', alert },
];
await store.dispatch(dismissAlert(alert));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
describe('showAlert()', () => {
it('dispatches the proper actions', async() => {
const title = 'title';
const message = 'msg';
const severity = 'info';
const expectedActions = [
{ type: 'ALERT_SHOW', title, message, severity },
];
await store.dispatch(showAlert(title, message, severity));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
describe('showAlert()', () => {
describe('with a 502 status code', () => {
it('dispatches the proper actions', async() => {
const message = 'The server is down';
const error = buildError(message, 502);
const expectedActions = [
{ type: 'ALERT_SHOW', title: '', message, severity: 'error' },
];
await store.dispatch(showAlertForError(error));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
describe('with a 404 status code', () => {
it('dispatches the proper actions', async() => {
const error = buildError('', 404);
const expectedActions = [];
await store.dispatch(showAlertForError(error));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
describe('with a 410 status code', () => {
it('dispatches the proper actions', async() => {
const error = buildError('', 410);
const expectedActions = [];
await store.dispatch(showAlertForError(error));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
describe('with an accepted status code', () => {
describe('with a message from the server', () => {
it('dispatches the proper actions', async() => {
const message = 'custom message';
const error = buildError(message, 200);
const expectedActions = [
{ type: 'ALERT_SHOW', title: '', message, severity: 'error' },
];
await store.dispatch(showAlertForError(error));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
describe('without a message from the server', () => {
it('dispatches the proper actions', async() => {
const message = 'The request has been accepted for processing';
const error = buildError(message, 202);
const expectedActions = [
{ type: 'ALERT_SHOW', title: '', message, severity: 'error' },
];
await store.dispatch(showAlertForError(error));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
});
describe('without a response', () => {
it('dispatches the proper actions', async() => {
const error = new AxiosError();
const expectedActions = [
{
type: 'ALERT_SHOW',
title: {
defaultMessage: 'Oops!',
id: 'alert.unexpected.title',
},
message: {
defaultMessage: 'An unexpected error occurred.',
id: 'alert.unexpected.message',
},
severity: 'error',
},
];
await store.dispatch(showAlertForError(error));
const actions = store.getActions();
expect(actions).toEqual(expectedActions);
});
});
});

View file

@ -1,68 +0,0 @@
import { defineMessages } from 'react-intl';
import { httpErrorMessages } from 'soapbox/utils/errors';
const messages = defineMessages({
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' },
});
export const ALERT_SHOW = 'ALERT_SHOW';
export const ALERT_DISMISS = 'ALERT_DISMISS';
export const ALERT_CLEAR = 'ALERT_CLEAR';
const noOp = () => {};
export function dismissAlert(alert) {
return {
type: ALERT_DISMISS,
alert,
};
}
export function clearAlert() {
return {
type: ALERT_CLEAR,
};
}
export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage, severity = 'info') {
return {
type: ALERT_SHOW,
title,
message,
severity,
};
}
export function showAlertForError(error) {
return (dispatch, _getState) => {
if (error.response) {
const { data, status, statusText } = error.response;
if (status === 502) {
return dispatch(showAlert('', 'The server is down', 'error'));
}
if (status === 404 || status === 410) {
// Skip these errors as they are reflected in the UI
return dispatch(noOp);
}
let message = statusText;
if (data.error) {
message = data.error;
}
if (!message) {
message = httpErrorMessages.find((httpError) => httpError.code === status)?.description;
}
return dispatch(showAlert('', message, 'error'));
} else {
console.error(error);
return dispatch(showAlert(undefined, undefined, 'error'));
}
};
}

View file

@ -0,0 +1,74 @@
import { AnyAction } from '@reduxjs/toolkit';
import { AxiosError } from 'axios';
import { defineMessages, MessageDescriptor } from 'react-intl';
import { httpErrorMessages } from 'soapbox/utils/errors';
import { SnackbarActionSeverity } from './snackbar';
const messages = defineMessages({
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' },
});
export const ALERT_SHOW = 'ALERT_SHOW';
export const ALERT_DISMISS = 'ALERT_DISMISS';
export const ALERT_CLEAR = 'ALERT_CLEAR';
const noOp = () => { };
function dismissAlert(alert: any) {
return {
type: ALERT_DISMISS,
alert,
};
}
function showAlert(
title: MessageDescriptor | string = messages.unexpectedTitle,
message: MessageDescriptor | string = messages.unexpectedMessage,
severity: SnackbarActionSeverity = 'info',
) {
return {
type: ALERT_SHOW,
title,
message,
severity,
};
}
const showAlertForError = (error: AxiosError<any>) => (dispatch: React.Dispatch<AnyAction>, _getState: any) => {
if (error.response) {
const { data, status, statusText } = error.response;
if (status === 502) {
return dispatch(showAlert('', 'The server is down', 'error'));
}
if (status === 404 || status === 410) {
// Skip these errors as they are reflected in the UI
return dispatch(noOp as any);
}
let message: string | undefined = statusText;
if (data.error) {
message = data.error;
}
if (!message) {
message = httpErrorMessages.find((httpError) => httpError.code === status)?.description;
}
return dispatch(showAlert('', message, 'error'));
} else {
console.error(error);
return dispatch(showAlert(undefined, undefined, 'error'));
}
};
export {
dismissAlert,
showAlert,
showAlertForError,
};

View file

@ -2,7 +2,7 @@ import { ALERT_SHOW } from './alerts';
import type { MessageDescriptor } from 'react-intl';
type SnackbarActionSeverity = 'info' | 'success' | 'error'
export type SnackbarActionSeverity = 'info' | 'success' | 'error'
type SnackbarMessage = string | MessageDescriptor