Adds Vuex store to handle the data for tests reports in MR widget
This commit is contained in:
parent
4636bebb68
commit
2d56c8fdd7
|
@ -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 () => {};
|
|
@ -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(),
|
||||||
|
});
|
|
@ -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';
|
|
@ -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;
|
||||||
|
},
|
||||||
|
};
|
|
@ -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: [],
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Adds Vuex store for reports section in MR widget
|
||||||
|
merge_request: 20709
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -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,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue