Project

General

Profile

« Previous | Next » 

Revision 7d6d573e

Added by Boaz Shuster almost 6 years ago

Fixes #23709 - Fix ajaxRequestAction failure dispatch

The FAILURE action dispatched in ajaxRequestAction is not
aligned with the bookmarks, powerStatus and statistics
reducers:
- Moving to axios returns Error object instead of a string
- The payload doesn't contain "id" but "item"

This patch fixes that problem and makes components work
on failures from API.

In addition, tests were added and fixed to avoid regressions.

Signed-off-by: Boaz Shuster <>

View differences:

webpack/assets/javascripts/react_app/components/statistics/StatisticsChartsList.js
chart={chart}
noDataMsg={__('No data available')}
tip={__('Expand the chart')}
errorText={chart.error}
errorText={chart.error && chart.error.message}
id={chart.id}
status={getStatusFromChart(chart)}
title={chart.title}
webpack/assets/javascripts/react_app/redux/actions/common/__snapshots__/ajaxRequestAction.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ajaxRequestAction should dispatch request and failure actions on reject 1`] = `
Array [
Array [
Object {
"payload": Object {
"id": "myid",
"name": "test",
},
"type": "REQUEST",
},
],
Array [
Object {
"payload": Object {
"error": [Error: bad request],
"item": Object {
"id": "myid",
"name": "test",
},
},
"type": "FAILURE",
},
],
]
`;
exports[`ajaxRequestAction should dispatch request and success actions on resolve 1`] = `
Array [
Array [
Object {
"payload": Object {
"id": "myid",
"name": "test",
},
"type": "REQUEST",
},
],
Array [
Object {
"payload": Object {
"id": "myid",
"name": "test",
"results": Array [
1,
],
},
"type": "SUCCESS",
},
],
]
`;
webpack/assets/javascripts/react_app/redux/actions/common/ajaxRequestAction.test.js
import { ajaxRequestAction } from './';
import API from '../../../API';
const data = { results: [1] };
const item = { name: 'test', id: 'myid' };
const requestAction = 'REQUEST';
const successAction = 'SUCCESS';
const failedAction = 'FAILURE';
describe('ajaxRequestAction', () => {
const setup = (dispatch, actionKey, actionValue) =>
ajaxRequestAction({
dispatch, [actionKey]: actionValue, requestAction, item,
})
.then(() => expect(dispatch.mock.calls).toMatchSnapshot());
let dispatch;
beforeEach(() => {
dispatch = jest.fn();
});
it('should dispatch request action first', () => {
const url = 'hosts/host1/memory';
ajaxRequestAction({
dispatch, requestAction, item, url,
});
expect(dispatch).toBeCalledWith({ type: requestAction, payload: item });
});
it('should dispatch request and success actions on resolve', () => {
API.get = jest.fn(url =>
new Promise((resolve, reject) => {
resolve({ data });
}));
return setup(dispatch, 'successAction', successAction);
});
it('should dispatch request and failure actions on reject', () => {
API.get = jest.fn(url =>
new Promise((resolve, reject) => {
reject(Error('bad request'));
}));
return setup(dispatch, 'failedAction', failedAction);
});
});
webpack/assets/javascripts/react_app/redux/reducers/hosts/powerStatus/__snapshots__/powerStatus.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`powerStatus reducer should handle HOST_POWER_STATUS_FAILURE 1`] = `
Object {
"2": Object {
"error": "some error happened",
},
}
`;
exports[`powerStatus reducer should handle HOST_POWER_STATUS_REQUEST 1`] = `
Object {
"2": Object {
"id": "2",
"url": "test",
},
}
`;
exports[`powerStatus reducer should handle HOST_POWER_STATUS_SUCCESS 1`] = `
Object {
"2": Object {
"data": "data",
"id": "2",
},
}
`;
exports[`powerStatus reducer should return the initial state 1`] = `Object {}`;
webpack/assets/javascripts/react_app/redux/reducers/hosts/powerStatus/index.js
);
case HOST_POWER_STATUS_FAILURE:
return state.set(
payload.id,
payload.item.id,
{ error: payload.error },
);
default:
webpack/assets/javascripts/react_app/redux/reducers/hosts/powerStatus/powerStatus.fixtures.js
export const stateBeforeResponse = Immutable({
[request.id]: request,
});
export const stateAfterSuccess = Immutable({
[request.id]: {
...response,
},
});
export const stateAfterFailure = Immutable({
[request.id]: {
error,
},
});
webpack/assets/javascripts/react_app/redux/reducers/hosts/powerStatus/powerStatus.test.js
request,
stateBeforeResponse,
response,
stateAfterSuccess,
stateAfterFailure,
error,
} from './powerStatus.fixtures';
import reducer from './index';
import { testReducerSnapshotWithFixtures } from '../../../../common/testHelpers';
describe('powerStatus reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual(initialState);
});
it('should handle HOST_POWER_STATUS_REQUEST', () => {
expect(reducer(initialState, {
type: types.HOST_POWER_STATUS_REQUEST,
payload: request,
})).toEqual(stateBeforeResponse);
});
it('should handle HOST_POWER_STATUS_SUCCESS', () => {
expect(reducer(stateBeforeResponse, {
type: types.HOST_POWER_STATUS_SUCCESS,
payload: response,
})).toEqual(stateAfterSuccess);
});
it('should handle HOST_POWER_STATUS_FAILURE', () => {
expect(reducer(stateBeforeResponse, {
type: types.HOST_POWER_STATUS_FAILURE,
payload: { error, id: request.id },
})).toEqual(stateAfterFailure);
});
const fixtures = {
'should return the initial state': {
state: undefined,
action: {},
},
'should handle HOST_POWER_STATUS_REQUEST': {
state: initialState,
action: {
type: types.HOST_POWER_STATUS_REQUEST,
payload: request,
},
},
'should handle HOST_POWER_STATUS_SUCCESS': {
state: stateBeforeResponse,
action: {
type: types.HOST_POWER_STATUS_SUCCESS,
payload: response,
},
},
'should handle HOST_POWER_STATUS_FAILURE': {
state: stateBeforeResponse,
action: {
type: types.HOST_POWER_STATUS_FAILURE,
payload: { error, item: { id: request.id } },
},
},
};
testReducerSnapshotWithFixtures(reducer, fixtures);
});
webpack/assets/javascripts/react_app/redux/reducers/statistics/__snapshots__/statistics.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`statistics reducer should handle STATISTICS_DATA_FAILURE 1`] = `
Object {
"charts": Object {
"operatingsystem": Object {
"error": "some error happened",
},
},
}
`;
exports[`statistics reducer should handle STATISTICS_DATA_REQUEST 1`] = `
Object {
"charts": Object {
"operatingsystem": Object {
"id": "operatingsystem",
"search": "/hosts?search=os_title=~VAL~",
"title": "OS Distribution",
"url": "statistics/operatingsystem",
},
},
}
`;
exports[`statistics reducer should handle STATISTICS_DATA_SUCCESS 1`] = `
Object {
"charts": Object {
"operatingsystem": Object {
"data": Array [
Array [
"RedHat 3",
2,
],
],
},
},
}
`;
exports[`statistics reducer should return the initial state 1`] = `
Object {
"charts": Object {},
}
`;
webpack/assets/javascripts/react_app/redux/reducers/statistics/index.js
data: payload.data,
});
case STATISTICS_DATA_FAILURE:
return state.setIn(['charts', payload.id], {
...state.charts[payload.id],
return state.setIn(['charts', payload.item.id], {
...state.charts[payload.item.id],
error: payload.error,
});
default:
webpack/assets/javascripts/react_app/redux/reducers/statistics/statistics.fixtures.js
[request.id]: request,
},
});
export const stateAfterSuccess = Immutable({
charts: Immutable({
[request.id]: {
...request,
data: response.data,
},
}),
});
export const stateAfterFailure = Immutable({
charts: Immutable({
[request.id]: {
...request,
error,
},
}),
});
webpack/assets/javascripts/react_app/redux/reducers/statistics/statistics.test.js
request,
stateBeforeResponse,
response,
stateAfterSuccess,
stateAfterFailure,
error,
} from './statistics.fixtures';
import reducer from './index';
import { testReducerSnapshotWithFixtures } from '../../../common/testHelpers';
describe('statistics reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual(initialState);
});
it('should handle STATISTICS_DATA_REQUEST', () => {
expect(reducer(initialState, {
type: types.STATISTICS_DATA_REQUEST,
payload: request,
})).toEqual(stateBeforeResponse);
});
it('should handle STATISTICS_DATA_SUCCESS', () => {
expect(reducer(stateBeforeResponse, {
type: types.STATISTICS_DATA_SUCCESS,
payload: response,
})).toEqual(stateAfterSuccess);
});
it('should handle STATISTICS_DATA_FAILURE', () => {
expect(reducer(stateBeforeResponse, {
type: types.STATISTICS_DATA_FAILURE,
payload: { error, id: request.id },
})).toEqual(stateAfterFailure);
});
const fixtures = {
'should return the initial state': {
prev: undefined,
action: {},
},
'should handle STATISTICS_DATA_REQUEST': {
prev: initialState,
action: {
type: types.STATISTICS_DATA_REQUEST,
payload: request,
},
},
'should handle STATISTICS_DATA_SUCCESS': {
prev: stateBeforeResponse,
action: {
type: types.STATISTICS_DATA_SUCCESS,
payload: response,
},
},
'should handle STATISTICS_DATA_FAILURE': {
prev: stateBeforeResponse,
action: {
type: types.STATISTICS_DATA_FAILURE,
payload: { error, item: request },
},
},
};
testReducerSnapshotWithFixtures(reducer, fixtures);
});

Also available in: Unified diff