Adds Vuex store to handle the data for tests reports in MR widget

This commit is contained in:
Filipa Lacerda - 🌴🌴OOO back on July 30th🌴🌴 2018-07-23 16:54:48 +00:00 committed by Phil Hughes
parent 4636bebb68
commit 2d56c8fdd7
8 changed files with 375 additions and 0 deletions

View File

@ -0,0 +1,67 @@
import Visibility from 'visibilityjs';
import axios from '../../lib/utils/axios_utils';
import Poll from '../../lib/utils/poll';
import * as types from './mutation_types';
export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
export const requestReports = ({ commit }) => commit(types.REQUEST_REPORTS);
let eTagPoll;
export const clearEtagPoll = () => {
eTagPoll = null;
};
export const stopPolling = () => {
if (eTagPoll) eTagPoll.stop();
};
export const restartPolling = () => {
if (eTagPoll) eTagPoll.restart();
};
/**
* We need to poll the reports endpoint while they are being parsed in the Backend.
* This can take up to one minute.
*
* Poll.js will handle etag response.
* While http status code is 204, it means it's parsing, and we'll keep polling
* When http status code is 200, it means parsing is done, we can show the results & stop polling
* When http status code is 500, it means parsing went wrong and we stop polling
*/
export const fetchReports = ({ state, dispatch }) => {
dispatch('requestReports');
eTagPoll = new Poll({
resource: {
getReports(endpoint) {
return axios.get(endpoint);
},
},
data: state.endpoint,
method: 'getReports',
successCallback: ({ data }) => dispatch('receiveReportsSuccess', data),
errorCallback: () => dispatch('receiveReportsError'),
});
if (!Visibility.hidden()) {
eTagPoll.makeRequest();
}
Visibility.change(() => {
if (!Visibility.hidden()) {
dispatch('restartPolling');
} else {
dispatch('stopPolling');
}
});
};
export const receiveReportsSuccess = ({ commit }, response) =>
commit(types.RECEIVE_REPORTS_SUCCESS, response);
export const receiveReportsError = ({ commit }) => commit(types.RECEIVE_REPORTS_ERROR);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};

View File

@ -0,0 +1,13 @@
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import mutations from './mutations';
import state from './state';
Vue.use(Vuex);
export default () => new Vuex.Store({
actions,
mutations,
state: state(),
});

View File

@ -0,0 +1,5 @@
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const REQUEST_REPORTS = 'REQUEST_REPORTS';
export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';

View File

@ -0,0 +1,26 @@
/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
export default {
[types.SET_ENDPOINT](state, endpoint) {
state.endpoint = endpoint;
},
[types.REQUEST_REPORTS](state) {
state.isLoading = true;
},
[types.RECEIVE_REPORTS_SUCCESS](state, response) {
state.isLoading = false;
state.summary.total = response.summary.total;
state.summary.resolved = response.summary.resolved;
state.summary.failed = response.summary.failed;
state.reports = response.suites;
},
[types.RECEIVE_REPORTS_ERROR](state) {
state.isLoading = false;
state.hasError = true;
},
};

View File

@ -0,0 +1,28 @@
export default () => ({
endpoint: null,
isLoading: false,
hasError: false,
summary: {
total: 0,
resolved: 0,
failed: 0,
},
/**
* Each report will have the following format:
* {
* name: {String},
* summary: {
* total: {Number},
* resolved: {Number},
* failed: {Number},
* },
* new_failures: {Array.<Object>},
* resolved_failures: {Array.<Object>},
* existing_failures: {Array.<Object>},
* }
*/
reports: [],
});

View File

@ -0,0 +1,5 @@
---
title: Adds Vuex store for reports section in MR widget
merge_request: 20709
author:
type: added

View File

@ -0,0 +1,130 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import {
setEndpoint,
requestReports,
fetchReports,
stopPolling,
clearEtagPoll,
receiveReportsSuccess,
receiveReportsError,
} from '~/reports/store/actions';
import state from '~/reports/store/state';
import * as types from '~/reports/store/mutation_types';
import testAction from 'spec/helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
describe('Reports Store Actions', () => {
let mockedState;
beforeEach(() => {
mockedState = state();
});
describe('setEndpoint', () => {
it('should commit SET_ENDPOINT mutation', done => {
testAction(
setEndpoint,
'endpoint.json',
mockedState,
[{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }],
[],
done,
);
});
});
describe('requestReports', () => {
it('should commit REQUEST_REPORTS mutation', done => {
testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done);
});
});
describe('fetchReports', () => {
let mock;
beforeEach(() => {
mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
stopPolling();
clearEtagPoll();
});
describe('success', () => {
it('dispatches requestReports and receiveReportsSuccess ', done => {
mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] });
testAction(
fetchReports,
null,
mockedState,
[],
[
{
type: 'requestReports',
},
{
payload: { summary: {}, suites: [{ name: 'rspec' }] },
type: 'receiveReportsSuccess',
},
],
done,
);
});
});
describe('error', () => {
beforeEach(() => {
mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
});
it('dispatches requestReports and receiveReportsError ', done => {
testAction(
fetchReports,
null,
mockedState,
[],
[
{
type: 'requestReports',
},
{
type: 'receiveReportsError',
},
],
done,
);
});
});
});
describe('receiveReportsSuccess', () => {
it('should commit RECEIVE_REPORTS_SUCCESS mutation', done => {
testAction(
receiveReportsSuccess,
{ summary: {} },
mockedState,
[{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }],
[],
done,
);
});
});
describe('receiveReportsError', () => {
it('should commit RECEIVE_REPORTS_ERROR mutation', done => {
testAction(
receiveReportsError,
null,
mockedState,
[{ type: types.RECEIVE_REPORTS_ERROR }],
[],
done,
);
});
});
});

View File

@ -0,0 +1,101 @@
import state from '~/reports/store/state';
import mutations from '~/reports/store/mutations';
import * as types from '~/reports/store/mutation_types';
describe('Reports Store Mutations', () => {
let stateCopy;
beforeEach(() => {
stateCopy = state();
});
describe('SET_ENDPOINT', () => {
it('should set endpoint', () => {
mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
expect(stateCopy.endpoint).toEqual('endpoint.json');
});
});
describe('REQUEST_REPORTS', () => {
it('should set isLoading to true', () => {
mutations[types.REQUEST_REPORTS](stateCopy);
expect(stateCopy.isLoading).toEqual(true);
});
});
describe('RECEIVE_REPORTS_SUCCESS', () => {
const mockedResponse = {
summary: {
total: 14,
resolved: 0,
failed: 7,
},
suites: [
{
name: 'build:linux',
summary: {
total: 2,
resolved: 0,
failed: 1,
},
new_failures: [
{
name: 'StringHelper#concatenate when a is git and b is lab returns summary',
execution_time: 0.0092435,
system_output:
'Failure/Error: is_expected.to eq(\'gitlab\')',
},
],
resolved_failures: [
{
name: 'StringHelper#concatenate when a is git and b is lab returns summary',
execution_time: 0.009235,
system_output:
'Failure/Error: is_expected.to eq(\'gitlab\')',
},
],
existing_failures: [
{
name: 'StringHelper#concatenate when a is git and b is lab returns summary',
execution_time: 1232.08,
system_output:
'Failure/Error: is_expected.to eq(\'gitlab\')',
},
],
},
],
};
beforeEach(() => {
mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse);
});
it('should reset isLoading', () => {
expect(stateCopy.isLoading).toEqual(false);
});
it('should set summary counts', () => {
expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total);
expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved);
expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed);
});
it('should set reports', () => {
expect(stateCopy.reports).toEqual(mockedResponse.suites);
});
});
describe('RECEIVE_REPORTS_ERROR', () => {
beforeEach(() => {
mutations[types.RECEIVE_REPORTS_ERROR](stateCopy);
});
it('should reset isLoading', () => {
expect(stateCopy.isLoading).toEqual(false);
});
it('should set hasError to true', () => {
expect(stateCopy.hasError).toEqual(true);
});
});
});