Commit 7f2f3f2c authored by Fatih Acet's avatar Fatih Acet

Merge branch 'jivl-migrate-dashboard-store-vuex' into 'master'

Migrate the monitoring dashboard store to vuex

See merge request gitlab-org/gitlab-ce!28555
parents 3cac033d 218dd512
......@@ -8,16 +8,14 @@ import {
GlLink,
} from '@gitlab/ui';
import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import '~/vue_shared/mixins/is_ee';
import { getParameterValues } from '~/lib/utils/url_utility';
import Flash from '../../flash';
import MonitoringService from '../services/monitoring_service';
import MonitorAreaChart from './charts/area.vue';
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store';
import { timeWindows, timeWindowsKeyNames } from '../constants';
import { getTimeDiff } from '../utils';
......@@ -128,9 +126,7 @@ export default {
},
data() {
return {
store: new MonitoringStore(),
state: 'gettingStarted',
showEmptyState: true,
elWidth: 0,
selectedTimeWindow: '',
selectedTimeWindowKey: '',
......@@ -141,13 +137,21 @@ export default {
canAddMetrics() {
return this.customMetricsAvailable && this.customMetricsPath.length;
},
...mapState('monitoringDashboard', [
'groups',
'emptyState',
'showEmptyState',
'environments',
'deploymentData',
]),
},
created() {
this.service = new MonitoringService({
this.setEndpoints({
metricsEndpoint: this.metricsEndpoint,
deploymentEndpoint: this.deploymentEndpoint,
environmentsEndpoint: this.environmentsEndpoint,
deploymentsEndpoint: this.deploymentEndpoint,
});
this.timeWindows = timeWindows;
this.selectedTimeWindowKey =
_.escape(getParameterValues('time_window')[0]) || timeWindowsKeyNames.eightHours;
......@@ -165,31 +169,11 @@ export default {
}
},
mounted() {
const startEndWindow = getTimeDiff(this.timeWindows[this.selectedTimeWindowKey]);
this.servicePromises = [
this.service
.getGraphsData(startEndWindow)
.then(data => this.store.storeMetrics(data))
.catch(() => Flash(s__('Metrics|There was an error while retrieving metrics'))),
this.service
.getDeploymentData()
.then(data => this.store.storeDeploymentData(data))
.catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))),
];
if (!this.hasMetrics) {
this.state = 'gettingStarted';
this.setGettingStartedEmptyState();
} else {
if (this.environmentsEndpoint) {
this.servicePromises.push(
this.service
.getEnvironmentsData()
.then(data => this.store.storeEnvironmentsData(data))
.catch(() =>
Flash(s__('Metrics|There was an error getting environments information.')),
),
);
}
this.getGraphsData();
this.fetchData(getTimeDiff(this.timeWindows.eightHours));
sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
attributes: true,
......@@ -199,6 +183,11 @@ export default {
}
},
methods: {
...mapActions('monitoringDashboard', [
'fetchData',
'setGettingStartedEmptyState',
'setEndpoints',
]),
getGraphAlerts(queries) {
if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId);
......@@ -207,21 +196,6 @@ export default {
getGraphAlertValues(queries) {
return Object.values(this.getGraphAlerts(queries));
},
getGraphsData() {
this.state = 'loading';
Promise.all(this.servicePromises)
.then(() => {
if (this.store.groups.length < 1) {
this.state = 'noData';
return;
}
this.showEmptyState = false;
})
.catch(() => {
this.state = 'unableToConnect';
});
},
hideAddMetricModal() {
this.$refs.addMetricModal.hide();
},
......@@ -263,10 +237,10 @@ export default {
class="prepend-left-10 js-environments-dropdown"
toggle-class="dropdown-menu-toggle"
:text="currentEnvironmentName"
:disabled="store.environmentsData.length === 0"
:disabled="environments.length === 0"
>
<gl-dropdown-item
v-for="environment in store.environmentsData"
v-for="environment in environments"
:key="environment.id"
:active="environment.name === currentEnvironmentName"
active-class="is-active"
......@@ -336,7 +310,7 @@ export default {
</div>
</div>
<graph-group
v-for="(groupData, index) in store.groups"
v-for="(groupData, index) in groups"
:key="index"
:name="groupData.group"
:show-panels="showPanels"
......@@ -345,7 +319,7 @@ export default {
v-for="(graphData, graphIndex) in groupData.metrics"
:key="graphIndex"
:graph-data="graphData"
:deployment-data="store.deploymentData"
:deployment-data="deploymentData"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="elWidth"
group-id="monitor-area-chart"
......@@ -362,7 +336,7 @@ export default {
</div>
<empty-state
v-else
:selected-state="state"
:selected-state="emptyState"
:documentation-path="documentationPath"
:settings-path="settingsPath"
:clusters-path="clustersPath"
......
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import Dashboard from 'ee_else_ce/monitoring/components/dashboard.vue';
import store from './stores';
export default (props = {}) => {
const el = document.getElementById('prometheus-graphs');
......@@ -9,6 +10,7 @@ export default (props = {}) => {
// eslint-disable-next-line no-new
new Vue({
el,
store,
render(createElement) {
return createElement(Dashboard, {
props: {
......
import axios from '../../lib/utils/axios_utils';
import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils';
import { s__, __ } from '../../locale';
const MAX_REQUESTS = 3;
function backOffRequest(makeRequestCallback) {
let requestCounter = 0;
return backOff((next, stop) => {
makeRequestCallback()
.then(resp => {
if (resp.status === statusCodes.NO_CONTENT) {
requestCounter += 1;
if (requestCounter < MAX_REQUESTS) {
next();
} else {
stop(new Error(__('Failed to connect to the prometheus server')));
}
} else {
stop(resp);
}
})
.catch(stop);
});
}
export default class MonitoringService {
constructor({ metricsEndpoint, deploymentEndpoint, environmentsEndpoint }) {
this.metricsEndpoint = metricsEndpoint;
this.deploymentEndpoint = deploymentEndpoint;
this.environmentsEndpoint = environmentsEndpoint;
}
getGraphsData(params = {}) {
return backOffRequest(() => axios.get(this.metricsEndpoint, { params }))
.then(resp => resp.data)
.then(response => {
if (!response || !response.data || !response.success) {
throw new Error(s__('Metrics|Unexpected metrics data response from prometheus endpoint'));
}
return response.data;
});
}
getDeploymentData() {
if (!this.deploymentEndpoint) {
return Promise.resolve([]);
}
return backOffRequest(() => axios.get(this.deploymentEndpoint))
.then(resp => resp.data)
.then(response => {
if (!response || !response.deployments) {
throw new Error(
s__('Metrics|Unexpected deployment data response from prometheus endpoint'),
);
}
return response.deployments;
});
}
getEnvironmentsData() {
return axios
.get(this.environmentsEndpoint)
.then(resp => resp.data)
.then(response => {
if (!response || !response.environments) {
throw new Error(
s__('Metrics|There was an error fetching the environments data, please try again'),
);
}
return response.environments;
});
}
}
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils';
import { s__, __ } from '../../locale';
const MAX_REQUESTS = 3;
function backOffRequest(makeRequestCallback) {
let requestCounter = 0;
return backOff((next, stop) => {
makeRequestCallback()
.then(resp => {
if (resp.status === statusCodes.NO_CONTENT) {
requestCounter += 1;
if (requestCounter < MAX_REQUESTS) {
next();
} else {
stop(new Error(__('Failed to connect to the prometheus server')));
}
} else {
stop(resp);
}
})
.catch(stop);
});
}
export const setGettingStartedEmptyState = ({ commit }) => {
commit(types.SET_GETTING_STARTED_EMPTY_STATE);
};
export const setEndpoints = ({ commit }, endpoints) => {
commit(types.SET_ENDPOINTS, endpoints);
};
export const requestMetricsData = ({ commit }) => commit(types.REQUEST_METRICS_DATA);
export const receiveMetricsDataSuccess = ({ commit }, data) =>
commit(types.RECEIVE_METRICS_DATA_SUCCESS, data);
export const receiveMetricsDataFailure = ({ commit }, error) =>
commit(types.RECEIVE_METRICS_DATA_FAILURE, error);
export const receiveDeploymentsDataSuccess = ({ commit }, data) =>
commit(types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS, data);
export const receiveDeploymentsDataFailure = ({ commit }) =>
commit(types.RECEIVE_DEPLOYMENTS_DATA_FAILURE);
export const receiveEnvironmentsDataSuccess = ({ commit }, data) =>
commit(types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, data);
export const receiveEnvironmentsDataFailure = ({ commit }) =>
commit(types.RECEIVE_ENVIRONMENTS_DATA_FAILURE);
export const fetchData = ({ dispatch }, params) => {
dispatch('fetchMetricsData', params);
dispatch('fetchDeploymentsData');
dispatch('fetchEnvironmentsData');
};
export const fetchMetricsData = ({ state, dispatch }, params) => {
dispatch('requestMetricsData');
return backOffRequest(() => axios.get(state.metricsEndpoint, { params }))
.then(resp => resp.data)
.then(response => {
if (!response || !response.data || !response.success) {
dispatch('receiveMetricsDataFailure', null);
createFlash(s__('Metrics|Unexpected metrics data response from prometheus endpoint'));
}
dispatch('receiveMetricsDataSuccess', response.data);
})
.catch(error => {
dispatch('receiveMetricsDataFailure', error);
createFlash(s__('Metrics|There was an error while retrieving metrics'));
});
};
export const fetchDeploymentsData = ({ state, dispatch }) => {
if (!state.deploymentEndpoint) {
return Promise.resolve([]);
}
return backOffRequest(() => axios.get(state.deploymentEndpoint))
.then(resp => resp.data)
.then(response => {
if (!response || !response.deployments) {
createFlash(s__('Metrics|Unexpected deployment data response from prometheus endpoint'));
}
dispatch('receiveDeploymentsDataSuccess', response.deployments);
})
.catch(() => {
dispatch('receiveDeploymentsDataFailure');
createFlash(s__('Metrics|There was an error getting deployment information.'));
});
};
export const fetchEnvironmentsData = ({ state, dispatch }) => {
if (!state.environmentsEndpoint) {
return Promise.resolve([]);
}
return axios
.get(state.environmentsEndpoint)
.then(resp => resp.data)
.then(response => {
if (!response || !response.environments) {
createFlash(
s__('Metrics|There was an error fetching the environments data, please try again'),
);
}
dispatch('receiveEnvironmentsDataSuccess', response.environments);
})
.catch(() => {
dispatch('receiveEnvironmentsDataFailure');
createFlash(s__('Metrics|There was an error getting environments information.'));
});
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
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 const createStore = () =>
new Vuex.Store({
modules: {
monitoringDashboard: {
namespaced: true,
actions,
mutations,
state,
},
},
});
export default createStore();
export const REQUEST_METRICS_DATA = 'REQUEST_METRICS_DATA';
export const RECEIVE_METRICS_DATA_SUCCESS = 'RECEIVE_METRICS_DATA_SUCCESS';
export const RECEIVE_METRICS_DATA_FAILURE = 'RECEIVE_METRICS_DATA_FAILURE';
export const REQUEST_DEPLOYMENTS_DATA = 'REQUEST_DEPLOYMENTS_DATA';
export const RECEIVE_DEPLOYMENTS_DATA_SUCCESS = 'RECEIVE_DEPLOYMENTS_DATA_SUCCESS';
export const RECEIVE_DEPLOYMENTS_DATA_FAILURE = 'RECEIVE_DEPLOYMENTS_DATA_FAILURE';
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCCESS';
export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAILURE';
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
export const SET_METRICS_ENDPOINT = 'SET_METRICS_ENDPOINT';
export const SET_ENVIRONMENTS_ENDPOINT = 'SET_ENVIRONMENTS_ENDPOINT';
export const SET_DEPLOYMENTS_ENDPOINT = 'SET_DEPLOYMENTS_ENDPOINT';
export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
import * as types from './mutation_types';
import { normalizeMetrics, sortMetrics } from './utils';
export default {
[types.REQUEST_METRICS_DATA](state) {
state.emptyState = 'loading';
state.showEmptyState = true;
},
[types.RECEIVE_METRICS_DATA_SUCCESS](state, groupData) {
state.groups = groupData.map(group => ({
...group,
metrics: normalizeMetrics(sortMetrics(group.metrics)),
}));
if (!state.groups.length) {
state.emptyState = 'noData';
} else {
state.showEmptyState = false;
}
},
[types.RECEIVE_METRICS_DATA_FAILURE](state, error) {
state.emptyState = error ? 'unableToConnect' : 'noData';
state.showEmptyState = true;
},
[types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS](state, deployments) {
state.deploymentData = deployments;
},
[types.RECEIVE_DEPLOYMENTS_DATA_FAILURE](state) {
state.deploymentData = [];
},
[types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS](state, environments) {
state.environments = environments;
},
[types.RECEIVE_ENVIRONMENTS_DATA_FAILURE](state) {
state.environments = [];
},
[types.SET_ENDPOINTS](state, endpoints) {
state.metricsEndpoint = endpoints.metricsEndpoint;
state.environmentsEndpoint = endpoints.environmentsEndpoint;
state.deploymentsEndpoint = endpoints.deploymentsEndpoint;
},
[types.SET_GETTING_STARTED_EMPTY_STATE](state) {
state.emptyState = 'gettingStarted';
},
};
export default () => ({
hasMetrics: false,
showPanels: true,
metricsEndpoint: null,
environmentsEndpoint: null,
deploymentsEndpoint: null,
emptyState: 'gettingStarted',
showEmptyState: true,
groups: [],
deploymentData: [],
environments: [],
});
import _ from 'underscore';
function sortMetrics(metrics) {
return _.chain(metrics)
.sortBy('title')
.sortBy('weight')
.value();
}
function checkQueryEmptyData(query) {
return {
...query,
......@@ -59,7 +52,13 @@ function groupQueriesByChartInfo(metrics) {
return Object.values(metricsByChart);
}
function normalizeMetrics(metrics) {
export const sortMetrics = metrics =>
_.chain(metrics)
.sortBy('title')
.sortBy('weight')
.value();
export const normalizeMetrics = metrics => {
const groupedMetrics = groupQueriesByChartInfo(metrics);
return groupedMetrics.map(metric => {
......@@ -81,31 +80,4 @@ function normalizeMetrics(metrics) {
queries: removeTimeSeriesNoData(queries),
};
});
}
export default class MonitoringStore {
constructor() {
this.groups = [];
this.deploymentData = [];
this.environmentsData = [];
}
storeMetrics(groups = []) {
this.groups = groups.map(group => ({
...group,
metrics: normalizeMetrics(sortMetrics(group.metrics)),
}));
}
storeDeploymentData(deploymentData = []) {
this.deploymentData = deploymentData;
}
storeEnvironmentsData(environmentsData = []) {
this.environmentsData = environmentsData.filter(environment => !!environment.last_deployment);
}
getMetricsCount() {
return this.groups.reduce((count, group) => count + group.metrics.length, 0);
}
}
};
---
title: Migrate the monitoring dashboard store to vuex
merge_request: 28555
author:
type: other
......@@ -2,7 +2,8 @@ import { shallowMount } from '@vue/test-utils';
import { GlAreaChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import { shallowWrapperContainsSlotText } from 'spec/helpers/vue_test_utils_helper';
import Area from '~/monitoring/components/charts/area.vue';
import MonitoringStore from '~/monitoring/stores/monitoring_store';
import { createStore } from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types';
import MonitoringMock, { deploymentData } from '../mock_data';
describe('Area component', () => {
......@@ -13,17 +14,18 @@ describe('Area component', () => {
let spriteSpy;
beforeEach(() => {
const store = new MonitoringStore();
store.storeMetrics(MonitoringMock.data);
store.storeDeploymentData(deploymentData);
const store = createStore();
[mockGraphData] = store.groups[0].metrics;
store.commit(`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, MonitoringMock.data);
store.commit(`monitoringDashboard/${types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS}`, deploymentData);
[mockGraphData] = store.state.monitoringDashboard.groups[0].metrics;
areaChart = shallowMount(Area, {
propsData: {
graphData: mockGraphData,
containerWidth: 0,
deploymentData: store.deploymentData,
deploymentData: store.state.monitoringDashboard.deploymentData,
},
slots: {
default: mockWidgets,
......
......@@ -2,8 +2,15 @@ import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint, environmentData } from './mock_data';
import {
metricsGroupsAPIResponse,
mockApiEndpoint,
environmentData,
singleGroupResponse,
} from './mock_data';
const propsData = {
hasMetrics: false,
......@@ -30,6 +37,7 @@ export default propsData;
describe('Dashboard', () => {
let DashboardComponent;
let mock;
let store;
beforeEach(() => {
setFixtures(`
......@@ -45,6 +53,7 @@ describe('Dashboard', () => {
},
};
store = createStore();
mock = new MockAdapter(axios);
DashboardComponent = Vue.extend(Dashboard);
});
......@@ -58,10 +67,11 @@ describe('Dashboard', () => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, showTimeWindowDropdown: false },
store,
});
expect(component.$el.querySelector('.prometheus-graphs')).toBe(null);
expect(component.state).toEqual('gettingStarted');
expect(component.emptyState).toEqual('gettingStarted');
});
});
......@@ -74,10 +84,11 @@ describe('Dashboard', () => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: false },
store,
});
Vue.nextTick(() => {
expect(component.state).toEqual('loading');
expect(component.emptyState).toEqual('loading');
done();
});
});
......@@ -91,6 +102,7 @@ describe('Dashboard', () => {
showLegend: false,
showTimeWindowDropdown: false,
},
store,
});
setTimeout(() => {
......@@ -110,6 +122,7 @@ describe('Dashboard', () => {
showPanels: false,
showTimeWindowDropdown: false,
},
store,
});
setTimeout(() => {
......@@ -129,16 +142,24 @@ describe('Dashboard', () => {
showPanels: false,
showTimeWindowDropdown: false,
},
store,
});
component.store.storeEnvironmentsData(environmentData);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
setTimeout(() => {
const dropdownMenuEnvironments = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item',
);
expect(dropdownMenuEnvironments.length).toEqual(component.store.environmentsData.length);
expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
done();
});
});
......@@ -152,18 +173,29 @@ describe('Dashboard', () => {
showPanels: false,
showTimeWindowDropdown: false,
},
store,
});
component.store.storeEnvironmentsData([]);
component.$store.commit(
`monitoringDashboard/${types.SET_ENVIRONMENTS_ENDPOINT}`,
'/environments',
);
component.$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
setTimeout(() => {
Vue.nextTick()
.then(() => {
const dropdownMenuEnvironments = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item',
);
expect(dropdownMenuEnvironments.length).toEqual(0);
done();
});
})
.catch(done.fail);
});
it('renders the environments dropdown with a single active element', done => {
......@@ -175,19 +207,32 @@ describe('Dashboard', () => {
showPanels: false,
showTimeWindowDropdown: false,
},
store,
});
component.store.storeEnvironmentsData(environmentData);
component.$store.commit(
`monitoringDashboard/${types.SET_ENVIRONMENTS_ENDPOINT}`,
'/environments',
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
setTimeout(() => {
Vue.nextTick()
.then(() => {
const dropdownItems = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item[active="true"]',
);
expect(dropdownItems.length).toEqual(1);
expect(dropdownItems[0].textContent.trim()).toEqual(component.currentEnvironmentName);
done();
});
})
.catch(done.fail);
});
it('hides the dropdown', done => {
......@@ -200,6 +245,7 @@ describe('Dashboard', () => {
environmentsEndpoint: '',
showTimeWindowDropdown: false,
},
store,
});
Vue.nextTick(() => {
......@@ -219,6 +265,7 @@ describe('Dashboard', () => {
showPanels: false,
showTimeWindowDropdown: false,
},
store,
});
setTimeout(() => {
......@@ -239,6 +286,7 @@ describe('Dashboard', () => {
showPanels: false,
showTimeWindowDropdown: true,
},
store,
});
const numberOfTimeWindows = Object.keys(timeWindows).length;
......@@ -261,6 +309,7 @@ describe('Dashboard', () => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: true },
store,
});
setTimeout(() => {
......@@ -281,6 +330,7 @@ describe('Dashboard', () => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: true },
store,
});
Vue.nextTick(() => {
......@@ -310,6 +360,7 @@ describe('Dashboard', () => {
showPanels: false,
showTimeWindowDropdown: false,
},
store,
});
expect(component.elWidth).toEqual(0);
......@@ -352,6 +403,7 @@ describe('Dashboard', () => {
showTimeWindowDropdown: false,
externalDashboardPath: '/mockPath',
},
store,
});
});
......@@ -377,6 +429,7 @@ describe('Dashboard', () => {
showTimeWindowDropdown: false,
externalDashboardPath: '',
},
store,
});
});
......
// eslint-disable-next-line import/prefer-default-export
export const resetStore = store => {
store.replaceState({
showEmptyState: true,
emptyState: 'loading',
groups: [],
});
};
This source diff could not be displayed because it is too large. You can view the blob instead.
import MonitoringStore from '~/monitoring/stores/monitoring_store';
import MonitoringMock, { deploymentData, environmentData } from './mock_data';
describe('MonitoringStore', () => {
const store = new MonitoringStore();
store.storeMetrics(MonitoringMock.data);
it('contains two groups that contains, one of which has two queries sorted by priority', () => {
expect(store.groups).toBeDefined();
expect(store.groups.length).toEqual(2);
expect(store.groups[0].metrics.length).toEqual(2);
});
it('gets the metrics count for every group', () => {
expect(store.getMetricsCount()).toEqual(3);
});
it('contains deployment data', () => {
store.storeDeploymentData(deploymentData);
expect(store.deploymentData).toBeDefined();
expect(store.deploymentData.length).toEqual(3);
expect(typeof store.deploymentData[0]).toEqual('object');
});
it('only stores environment data that contains deployments', () => {
store.storeEnvironmentsData(environmentData);
expect(store.environmentsData.length).toEqual(2);
});
it('removes the data if all the values from a query are not defined', () => {
expect(store.groups[1].metrics[0].queries[0].result.length).toEqual(0);
});
it('assigns queries a metric id', () => {
expect(store.groups[1].metrics[0].queries[0].metricId).toEqual('100');
});
it('assigns metric id of null if metric has no id', () => {
const noId = MonitoringMock.data.map(group => ({
...group,
...{
metrics: group.metrics.map(metric => {
const { id, ...metricWithoutId } = metric;
return metricWithoutId;
}),
},
}));
store.storeMetrics(noId);
store.groups.forEach(group => {
group.metrics.forEach(metric => {
expect(metric.queries.every(query => query.metricId === null)).toBe(true);
});
});
});
});
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import store from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types';
import {
fetchDeploymentsData,
fetchEnvironmentsData,
requestMetricsData,
setEndpoints,
setGettingStartedEmptyState,
} from '~/monitoring/stores/actions';
import storeState from '~/monitoring/stores/state';
import testAction from 'spec/helpers/vuex_action_helper';
import { resetStore } from '../helpers';
import { deploymentData, environmentData } from '../mock_data';
describe('Monitoring store actions', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
resetStore(store);
mock.restore();
});
describe('requestMetricsData', () => {
it('sets emptyState to loading', () => {
const commit = jasmine.createSpy();
const { state } = store;
requestMetricsData({ state, commit });
expect(commit).toHaveBeenCalledWith(types.REQUEST_METRICS_DATA);
});
});
describe('fetchDeploymentsData', () => {
it('commits RECEIVE_DEPLOYMENTS_DATA_SUCCESS on error', done => {
const dispatch = jasmine.createSpy();
const { state } = store;
state.deploymentEndpoint = '/success';
mock.onGet(state.deploymentEndpoint).reply(200, {
deployments: deploymentData,
});
fetchDeploymentsData({ state, dispatch })
.then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveDeploymentsDataSuccess', deploymentData);
done();
})
.catch(done.fail);
});
it('commits RECEIVE_DEPLOYMENTS_DATA_FAILURE on error', done => {
const dispatch = jasmine.createSpy();
const { state } = store;
state.deploymentEndpoint = '/error';
mock.onGet(state.deploymentEndpoint).reply(500);
fetchDeploymentsData({ state, dispatch })
.then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveDeploymentsDataFailure');
done();
})
.catch(done.fail);
});
});
describe('fetchEnvironmentsData', () => {
it('commits RECEIVE_ENVIRONMENTS_DATA_SUCCESS on error', done => {
const dispatch = jasmine.createSpy();
const { state } = store;
state.environmentsEndpoint = '/success';
mock.onGet(state.environmentsEndpoint).reply(200, {
environments: environmentData,
});
fetchEnvironmentsData({ state, dispatch })
.then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataSuccess', environmentData);
done();
})
.catch(done.fail);
});
it('commits RECEIVE_ENVIRONMENTS_DATA_FAILURE on error', done => {
const dispatch = jasmine.createSpy();
const { state } = store;
state.environmentsEndpoint = '/error';
mock.onGet(state.environmentsEndpoint).reply(500);
fetchEnvironmentsData({ state, dispatch })
.then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataFailure');
done();
})
.catch(done.fail);
});
});
describe('Set endpoints', () => {
let mockedState;
beforeEach(() => {
mockedState = storeState();
});
it('should commit SET_ENDPOINTS mutation', done => {
testAction(
setEndpoints,
{
metricsEndpoint: 'additional_metrics.json',
deploymentsEndpoint: 'deployments.json',
environmentsEndpoint: 'deployments.json',
},
mockedState,
[
{
type: types.SET_ENDPOINTS,
payload: {
metricsEndpoint: 'additional_metrics.json',
deploymentsEndpoint: 'deployments.json',
environmentsEndpoint: 'deployments.json',
},
},
],
[],
done,
);
});
});
describe('Set empty states', () => {
let mockedState;
beforeEach(() => {
mockedState = storeState();
});
it('should commit SET_METRICS_ENDPOINT mutation', done => {
testAction(
setGettingStartedEmptyState,
null,
mockedState,
[{ type: types.SET_GETTING_STARTED_EMPTY_STATE }],
[],
done,
);
});
});
});
import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types';
import state from '~/monitoring/stores/state';
import { metricsGroupsAPIResponse, deploymentData } from '../mock_data';
describe('Monitoring mutations', () => {
let stateCopy;
beforeEach(() => {
stateCopy = state();
});
describe(types.RECEIVE_METRICS_DATA_SUCCESS, () => {
beforeEach(() => {
stateCopy.groups = [];
const groups = metricsGroupsAPIResponse.data;
mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups);
});
it('normalizes values', () => {
const expectedTimestamp = '2017-05-25T08:22:34.925Z';
const expectedValue = 0.0010794445585559514;
const [timestamp, value] = stateCopy.groups[0].metrics[0].queries[0].result[0].values[0];
expect(timestamp).toEqual(expectedTimestamp);
expect(value).toEqual(expectedValue);
});
it('contains two groups that contains, one of which has two queries sorted by priority', () => {
expect(stateCopy.groups).toBeDefined();
expect(stateCopy.groups.length).toEqual(2);
expect(stateCopy.groups[0].metrics.length).toEqual(2);
});
it('assigns queries a metric id', () => {
expect(stateCopy.groups[1].metrics[0].queries[0].metricId).toEqual('100');
});
it('removes the data if all the values from a query are not defined', () => {
expect(stateCopy.groups[1].metrics[0].queries[0].result.length).toEqual(0);
});
it('assigns metric id of null if metric has no id', () => {
stateCopy.groups = [];
const groups = metricsGroupsAPIResponse.data;
const noId = groups.map(group => ({
...group,
...{
metrics: group.metrics.map(metric => {
const { id, ...metricWithoutId } = metric;
return metricWithoutId;
}),
},
}));
mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, noId);
stateCopy.groups.forEach(group => {
group.metrics.forEach(metric => {
expect(metric.queries.every(query => query.metricId === null)).toBe(true);
});
});
});
});
describe(types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS, () => {
it('stores the deployment data', () => {
stateCopy.deploymentData = [];
mutations[types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS](stateCopy, deploymentData);
expect(stateCopy.deploymentData).toBeDefined();
expect(stateCopy.deploymentData.length).toEqual(3);
expect(typeof stateCopy.deploymentData[0]).toEqual('object');
});
});
describe('SET_ENDPOINTS', () => {
it('should set all the endpoints', () => {
mutations[types.SET_ENDPOINTS](stateCopy, {
metricsEndpoint: 'additional_metrics.json',
environmentsEndpoint: 'environments.json',
deploymentsEndpoint: 'deployments.json',
});
expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json');
expect(stateCopy.environmentsEndpoint).toEqual('environments.json');
expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json');
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment