summaryrefslogtreecommitdiff
path: root/spec/frontend/monitoring/store/actions_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/monitoring/store/actions_spec.js')
-rw-r--r--spec/frontend/monitoring/store/actions_spec.js980
1 files changed, 590 insertions, 390 deletions
diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js
index d0290386f12..22f2b2e3c77 100644
--- a/spec/frontend/monitoring/store/actions_spec.js
+++ b/spec/frontend/monitoring/store/actions_spec.js
@@ -6,27 +6,30 @@ import statusCodes from '~/lib/utils/http_status';
import * as commonUtils from '~/lib/utils/common_utils';
import createFlash from '~/flash';
import { defaultTimeRange } from '~/vue_shared/constants';
+import * as getters from '~/monitoring/stores/getters';
import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants';
import { createStore } from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types';
import {
+ setGettingStartedEmptyState,
+ setInitialState,
+ setExpandedPanel,
+ clearExpandedPanel,
+ filterEnvironments,
fetchData,
fetchDashboard,
receiveMetricsDashboardSuccess,
+ fetchDashboardData,
+ fetchPrometheusMetric,
fetchDeploymentsData,
fetchEnvironmentsData,
- fetchDashboardData,
fetchAnnotations,
+ fetchDashboardValidationWarnings,
toggleStarredValue,
- fetchPrometheusMetric,
- setInitialState,
- filterEnvironments,
- setExpandedPanel,
- clearExpandedPanel,
- setGettingStartedEmptyState,
duplicateSystemDashboard,
updateVariablesAndFetchData,
+ fetchVariableMetricLabelValues,
} from '~/monitoring/stores/actions';
import {
gqClient,
@@ -35,12 +38,12 @@ import {
} from '~/monitoring/stores/utils';
import getEnvironments from '~/monitoring/queries/getEnvironments.query.graphql';
import getAnnotations from '~/monitoring/queries/getAnnotations.query.graphql';
+import getDashboardValidationWarnings from '~/monitoring/queries/getDashboardValidationWarnings.query.graphql';
import storeState from '~/monitoring/stores/state';
import {
deploymentData,
environmentData,
annotationsData,
- mockTemplatingData,
dashboardGitResponse,
mockDashboardsErrorResponse,
} from '../mock_data';
@@ -59,11 +62,17 @@ describe('Monitoring store actions', () => {
let store;
let state;
+ let dispatch;
+ let commit;
+
beforeEach(() => {
- store = createStore();
+ store = createStore({ getters });
state = store.state.monitoringDashboard;
mock = new MockAdapter(axios);
+ commit = jest.fn();
+ dispatch = jest.fn();
+
jest.spyOn(commonUtils, 'backOff').mockImplementation(callback => {
const q = new Promise((resolve, reject) => {
const stop = arg => (arg instanceof Error ? reject(arg) : resolve(arg));
@@ -78,6 +87,7 @@ describe('Monitoring store actions', () => {
return q;
});
});
+
afterEach(() => {
mock.reset();
@@ -85,377 +95,122 @@ describe('Monitoring store actions', () => {
createFlash.mockReset();
});
- describe('fetchData', () => {
- it('dispatches fetchEnvironmentsData and fetchEnvironmentsData', () => {
- return testAction(
- fetchData,
- null,
- state,
- [],
- [
- { type: 'fetchEnvironmentsData' },
- { type: 'fetchDashboard' },
- { type: 'fetchAnnotations' },
- ],
- );
- });
+ // Setup
- it('dispatches when feature metricsDashboardAnnotations is on', () => {
- const origGon = window.gon;
- window.gon = { features: { metricsDashboardAnnotations: true } };
-
- return testAction(
- fetchData,
+ describe('setGettingStartedEmptyState', () => {
+ it('should commit SET_GETTING_STARTED_EMPTY_STATE mutation', done => {
+ testAction(
+ setGettingStartedEmptyState,
null,
state,
- [],
[
- { type: 'fetchEnvironmentsData' },
- { type: 'fetchDashboard' },
- { type: 'fetchAnnotations' },
+ {
+ type: types.SET_GETTING_STARTED_EMPTY_STATE,
+ },
],
- ).then(() => {
- window.gon = origGon;
- });
- });
- });
-
- describe('fetchDeploymentsData', () => {
- it('dispatches receiveDeploymentsDataSuccess on success', () => {
- state.deploymentsEndpoint = '/success';
- mock.onGet(state.deploymentsEndpoint).reply(200, {
- deployments: deploymentData,
- });
-
- return testAction(
- fetchDeploymentsData,
- null,
- state,
- [],
- [{ type: 'receiveDeploymentsDataSuccess', payload: deploymentData }],
- );
- });
- it('dispatches receiveDeploymentsDataFailure on error', () => {
- state.deploymentsEndpoint = '/error';
- mock.onGet(state.deploymentsEndpoint).reply(500);
-
- return testAction(
- fetchDeploymentsData,
- null,
- state,
[],
- [{ type: 'receiveDeploymentsDataFailure' }],
- () => {
- expect(createFlash).toHaveBeenCalled();
- },
+ done,
);
});
});
- describe('fetchEnvironmentsData', () => {
- beforeEach(() => {
- state.projectPath = 'gitlab-org/gitlab-test';
- });
-
- it('setting SET_ENVIRONMENTS_FILTER should dispatch fetchEnvironmentsData', () => {
- jest.spyOn(gqClient, 'mutate').mockReturnValue({
- data: {
- project: {
- data: {
- environments: [],
- },
- },
+ describe('setInitialState', () => {
+ it('should commit SET_INITIAL_STATE mutation', done => {
+ testAction(
+ setInitialState,
+ {
+ currentDashboard: '.gitlab/dashboards/dashboard.yml',
+ deploymentsEndpoint: 'deployments.json',
},
- });
-
- return testAction(
- filterEnvironments,
- {},
state,
[
{
- type: 'SET_ENVIRONMENTS_FILTER',
- payload: {},
- },
- ],
- [
- {
- type: 'fetchEnvironmentsData',
+ type: types.SET_INITIAL_STATE,
+ payload: {
+ currentDashboard: '.gitlab/dashboards/dashboard.yml',
+ deploymentsEndpoint: 'deployments.json',
+ },
},
],
- );
- });
-
- it('fetch environments data call takes in search param', () => {
- const mockMutate = jest.spyOn(gqClient, 'mutate');
- const searchTerm = 'Something';
- const mutationVariables = {
- mutation: getEnvironments,
- variables: {
- projectPath: state.projectPath,
- search: searchTerm,
- states: [ENVIRONMENT_AVAILABLE_STATE],
- },
- };
- state.environmentsSearchTerm = searchTerm;
- mockMutate.mockResolvedValue({});
-
- return testAction(
- fetchEnvironmentsData,
- null,
- state,
[],
- [
- { type: 'requestEnvironmentsData' },
- { type: 'receiveEnvironmentsDataSuccess', payload: [] },
- ],
- () => {
- expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
- },
+ done,
);
});
+ });
- it('dispatches receiveEnvironmentsDataSuccess on success', () => {
- jest.spyOn(gqClient, 'mutate').mockResolvedValue({
- data: {
- project: {
- data: {
- environments: environmentData,
- },
- },
- },
- });
+ describe('setExpandedPanel', () => {
+ it('Sets a panel as expanded', () => {
+ const group = 'group_1';
+ const panel = { title: 'A Panel' };
return testAction(
- fetchEnvironmentsData,
- null,
+ setExpandedPanel,
+ { group, panel },
state,
+ [{ type: types.SET_EXPANDED_PANEL, payload: { group, panel } }],
[],
- [
- { type: 'requestEnvironmentsData' },
- {
- type: 'receiveEnvironmentsDataSuccess',
- payload: parseEnvironmentsResponse(environmentData, state.projectPath),
- },
- ],
);
});
+ });
- it('dispatches receiveEnvironmentsDataFailure on error', () => {
- jest.spyOn(gqClient, 'mutate').mockRejectedValue({});
-
+ describe('clearExpandedPanel', () => {
+ it('Clears a panel as expanded', () => {
return testAction(
- fetchEnvironmentsData,
- null,
+ clearExpandedPanel,
+ undefined,
state,
+ [{ type: types.SET_EXPANDED_PANEL, payload: { group: null, panel: null } }],
[],
- [{ type: 'requestEnvironmentsData' }, { type: 'receiveEnvironmentsDataFailure' }],
);
});
});
- describe('fetchAnnotations', () => {
- beforeEach(() => {
- state.timeRange = {
- start: '2020-04-15T12:54:32.137Z',
- end: '2020-08-15T12:54:32.137Z',
- };
- state.projectPath = 'gitlab-org/gitlab-test';
- state.currentEnvironmentName = 'production';
- state.currentDashboard = '.gitlab/dashboards/custom_dashboard.yml';
- });
-
- it('fetches annotations data and dispatches receiveAnnotationsSuccess', () => {
- const mockMutate = jest.spyOn(gqClient, 'mutate');
- const mutationVariables = {
- mutation: getAnnotations,
- variables: {
- projectPath: state.projectPath,
- environmentName: state.currentEnvironmentName,
- dashboardPath: state.currentDashboard,
- startingFrom: state.timeRange.start,
- },
- };
- const parsedResponse = parseAnnotationsResponse(annotationsData);
-
- mockMutate.mockResolvedValue({
- data: {
- project: {
- environments: {
- nodes: [
- {
- metricsDashboard: {
- annotations: {
- nodes: parsedResponse,
- },
- },
- },
- ],
- },
- },
- },
- });
+ // All Data
+ describe('fetchData', () => {
+ it('dispatches fetchEnvironmentsData and fetchEnvironmentsData', () => {
return testAction(
- fetchAnnotations,
+ fetchData,
null,
state,
[],
- [{ type: 'receiveAnnotationsSuccess', payload: parsedResponse }],
- () => {
- expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
- },
+ [
+ { type: 'fetchEnvironmentsData' },
+ { type: 'fetchDashboard' },
+ { type: 'fetchAnnotations' },
+ ],
);
});
- it('dispatches receiveAnnotationsFailure if the annotations API call fails', () => {
- const mockMutate = jest.spyOn(gqClient, 'mutate');
- const mutationVariables = {
- mutation: getAnnotations,
- variables: {
- projectPath: state.projectPath,
- environmentName: state.currentEnvironmentName,
- dashboardPath: state.currentDashboard,
- startingFrom: state.timeRange.start,
- },
- };
-
- mockMutate.mockRejectedValue({});
+ it('dispatches when feature metricsDashboardAnnotations is on', () => {
+ const origGon = window.gon;
+ window.gon = { features: { metricsDashboardAnnotations: true } };
return testAction(
- fetchAnnotations,
+ fetchData,
null,
state,
[],
- [{ type: 'receiveAnnotationsFailure' }],
- () => {
- expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
- },
- );
- });
- });
-
- describe('Toggles starred value of current dashboard', () => {
- let unstarredDashboard;
- let starredDashboard;
-
- beforeEach(() => {
- state.isUpdatingStarredValue = false;
- [unstarredDashboard, starredDashboard] = dashboardGitResponse;
- });
-
- describe('toggleStarredValue', () => {
- it('performs no changes if no dashboard is selected', () => {
- return testAction(toggleStarredValue, null, state, [], []);
- });
-
- it('performs no changes if already changing starred value', () => {
- state.selectedDashboard = unstarredDashboard;
- state.isUpdatingStarredValue = true;
- return testAction(toggleStarredValue, null, state, [], []);
- });
-
- it('stars dashboard if it is not starred', () => {
- state.selectedDashboard = unstarredDashboard;
- mock.onPost(unstarredDashboard.user_starred_path).reply(200);
-
- return testAction(toggleStarredValue, null, state, [
- { type: types.REQUEST_DASHBOARD_STARRING },
- {
- type: types.RECEIVE_DASHBOARD_STARRING_SUCCESS,
- payload: {
- newStarredValue: true,
- selectedDashboard: unstarredDashboard,
- },
- },
- ]);
- });
-
- it('unstars dashboard if it is starred', () => {
- state.selectedDashboard = starredDashboard;
- mock.onPost(starredDashboard.user_starred_path).reply(200);
-
- return testAction(toggleStarredValue, null, state, [
- { type: types.REQUEST_DASHBOARD_STARRING },
- { type: types.RECEIVE_DASHBOARD_STARRING_FAILURE },
- ]);
- });
- });
- });
-
- describe('Set initial state', () => {
- it('should commit SET_INITIAL_STATE mutation', done => {
- testAction(
- setInitialState,
- {
- currentDashboard: '.gitlab/dashboards/dashboard.yml',
- deploymentsEndpoint: 'deployments.json',
- },
- state,
- [
- {
- type: types.SET_INITIAL_STATE,
- payload: {
- currentDashboard: '.gitlab/dashboards/dashboard.yml',
- deploymentsEndpoint: 'deployments.json',
- },
- },
- ],
- [],
- done,
- );
- });
- });
- describe('Set empty states', () => {
- it('should commit SET_METRICS_ENDPOINT mutation', done => {
- testAction(
- setGettingStartedEmptyState,
- null,
- state,
[
- {
- type: types.SET_GETTING_STARTED_EMPTY_STATE,
- },
+ { type: 'fetchEnvironmentsData' },
+ { type: 'fetchDashboard' },
+ { type: 'fetchAnnotations' },
],
- [],
- done,
- );
+ ).then(() => {
+ window.gon = origGon;
+ });
});
});
- describe('updateVariablesAndFetchData', () => {
- it('should commit UPDATE_VARIABLES mutation and fetch data', done => {
- testAction(
- updateVariablesAndFetchData,
- { pod: 'POD' },
- state,
- [
- {
- type: types.UPDATE_VARIABLES,
- payload: { pod: 'POD' },
- },
- ],
- [
- {
- type: 'fetchDashboardData',
- },
- ],
- done,
- );
- });
- });
+ // Metrics dashboard
describe('fetchDashboard', () => {
- let dispatch;
- let commit;
const response = metricsDashboardResponse;
beforeEach(() => {
- dispatch = jest.fn();
- commit = jest.fn();
state.dashboardEndpoint = '/dashboard';
});
- it('on success, dispatches receive and success actions', () => {
+ it('on success, dispatches receive and success actions, then fetches dashboard warnings', () => {
document.body.dataset.page = 'projects:environments:metrics';
mock.onGet(state.dashboardEndpoint).reply(200, response);
@@ -470,6 +225,7 @@ describe('Monitoring store actions', () => {
type: 'receiveMetricsDashboardSuccess',
payload: { response },
},
+ { type: 'fetchDashboardValidationWarnings' },
],
);
});
@@ -478,9 +234,12 @@ describe('Monitoring store actions', () => {
let result;
beforeEach(() => {
const params = {};
+ const localGetters = {
+ fullDashboardPath: store.getters['monitoringDashboard/fullDashboardPath'],
+ };
result = () => {
mock.onGet(state.dashboardEndpoint).replyOnce(500, mockDashboardsErrorResponse);
- return fetchDashboard({ state, commit, dispatch }, params);
+ return fetchDashboard({ state, commit, dispatch, getters: localGetters }, params);
};
});
@@ -532,15 +291,8 @@ describe('Monitoring store actions', () => {
});
});
});
- describe('receiveMetricsDashboardSuccess', () => {
- let commit;
- let dispatch;
-
- beforeEach(() => {
- commit = jest.fn();
- dispatch = jest.fn();
- });
+ describe('receiveMetricsDashboardSuccess', () => {
it('stores groups', () => {
const response = metricsDashboardResponse;
receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response });
@@ -552,32 +304,6 @@ describe('Monitoring store actions', () => {
expect(dispatch).toHaveBeenCalledWith('fetchDashboardData');
});
- it('stores templating variables', () => {
- const response = {
- ...metricsDashboardResponse.dashboard,
- ...mockTemplatingData.allVariableTypes.dashboard,
- };
-
- receiveMetricsDashboardSuccess(
- { state, commit, dispatch },
- {
- response: {
- ...metricsDashboardResponse,
- dashboard: {
- ...metricsDashboardResponse.dashboard,
- ...mockTemplatingData.allVariableTypes.dashboard,
- },
- },
- },
- );
-
- expect(commit).toHaveBeenCalledWith(
- types.RECEIVE_METRICS_DASHBOARD_SUCCESS,
-
- response,
- );
- });
-
it('sets the dashboards loaded from the repository', () => {
const params = {};
const response = metricsDashboardResponse;
@@ -596,23 +322,21 @@ describe('Monitoring store actions', () => {
expect(commit).toHaveBeenCalledWith(types.SET_ALL_DASHBOARDS, dashboardGitResponse);
});
});
- describe('fetchDashboardData', () => {
- let commit;
- let dispatch;
+ // Metrics
+
+ describe('fetchDashboardData', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');
- commit = jest.fn();
- dispatch = jest.fn();
state.timeRange = defaultTimeRange;
});
it('commits empty state when state.groups is empty', done => {
- const getters = {
+ const localGetters = {
metricsWithData: () => [],
};
- fetchDashboardData({ state, commit, dispatch, getters })
+ fetchDashboardData({ state, commit, dispatch, getters: localGetters })
.then(() => {
expect(Tracking.event).toHaveBeenCalledWith(
document.body.dataset.page,
@@ -623,25 +347,33 @@ describe('Monitoring store actions', () => {
value: 0,
},
);
- expect(dispatch).toHaveBeenCalledTimes(1);
+ expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenCalledWith('fetchDeploymentsData');
+ expect(dispatch).toHaveBeenCalledWith('fetchVariableMetricLabelValues', {
+ defaultQueryParams: {
+ start_time: expect.any(String),
+ end_time: expect.any(String),
+ step: expect.any(Number),
+ },
+ });
expect(createFlash).not.toHaveBeenCalled();
done();
})
.catch(done.fail);
});
+
it('dispatches fetchPrometheusMetric for each panel query', done => {
state.dashboard.panelGroups = convertObjectPropsToCamelCase(
metricsDashboardResponse.dashboard.panel_groups,
);
const [metric] = state.dashboard.panelGroups[0].panels[0].metrics;
- const getters = {
+ const localGetters = {
metricsWithData: () => [metric.id],
};
- fetchDashboardData({ state, commit, dispatch, getters })
+ fetchDashboardData({ state, commit, dispatch, getters: localGetters })
.then(() => {
expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetric', {
metric,
@@ -673,21 +405,27 @@ describe('Monitoring store actions', () => {
const metric = state.dashboard.panelGroups[0].panels[0].metrics[0];
dispatch.mockResolvedValueOnce(); // fetchDeploymentsData
+ dispatch.mockResolvedValueOnce(); // fetchVariableMetricLabelValues
// Mock having one out of four metrics failing
dispatch.mockRejectedValueOnce(new Error('Error fetching this metric'));
dispatch.mockResolvedValue();
fetchDashboardData({ state, commit, dispatch })
.then(() => {
- expect(dispatch).toHaveBeenCalledTimes(metricsDashboardPanelCount + 1); // plus 1 for deployments
+ const defaultQueryParams = {
+ start_time: expect.any(String),
+ end_time: expect.any(String),
+ step: expect.any(Number),
+ };
+
+ expect(dispatch).toHaveBeenCalledTimes(metricsDashboardPanelCount + 2); // plus 1 for deployments
expect(dispatch).toHaveBeenCalledWith('fetchDeploymentsData');
+ expect(dispatch).toHaveBeenCalledWith('fetchVariableMetricLabelValues', {
+ defaultQueryParams,
+ });
expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetric', {
metric,
- defaultQueryParams: {
- start_time: expect.any(String),
- end_time: expect.any(String),
- step: expect.any(Number),
- },
+ defaultQueryParams,
});
expect(createFlash).toHaveBeenCalledTimes(1);
@@ -698,6 +436,7 @@ describe('Monitoring store actions', () => {
done();
});
});
+
describe('fetchPrometheusMetric', () => {
const defaultQueryParams = {
start_time: '2019-08-06T12:40:02.184Z',
@@ -738,7 +477,7 @@ describe('Monitoring store actions', () => {
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
- result: data.result,
+ data,
},
},
],
@@ -775,7 +514,7 @@ describe('Monitoring store actions', () => {
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
- result: data.result,
+ data,
},
},
],
@@ -817,7 +556,7 @@ describe('Monitoring store actions', () => {
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
- result: data.result,
+ data,
},
},
],
@@ -852,7 +591,7 @@ describe('Monitoring store actions', () => {
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
- result: data.result,
+ data,
},
},
],
@@ -901,6 +640,402 @@ describe('Monitoring store actions', () => {
});
});
+ // Deployments
+
+ describe('fetchDeploymentsData', () => {
+ it('dispatches receiveDeploymentsDataSuccess on success', () => {
+ state.deploymentsEndpoint = '/success';
+ mock.onGet(state.deploymentsEndpoint).reply(200, {
+ deployments: deploymentData,
+ });
+
+ return testAction(
+ fetchDeploymentsData,
+ null,
+ state,
+ [],
+ [{ type: 'receiveDeploymentsDataSuccess', payload: deploymentData }],
+ );
+ });
+ it('dispatches receiveDeploymentsDataFailure on error', () => {
+ state.deploymentsEndpoint = '/error';
+ mock.onGet(state.deploymentsEndpoint).reply(500);
+
+ return testAction(
+ fetchDeploymentsData,
+ null,
+ state,
+ [],
+ [{ type: 'receiveDeploymentsDataFailure' }],
+ () => {
+ expect(createFlash).toHaveBeenCalled();
+ },
+ );
+ });
+ });
+
+ // Environments
+
+ describe('fetchEnvironmentsData', () => {
+ beforeEach(() => {
+ state.projectPath = 'gitlab-org/gitlab-test';
+ });
+
+ it('setting SET_ENVIRONMENTS_FILTER should dispatch fetchEnvironmentsData', () => {
+ jest.spyOn(gqClient, 'mutate').mockReturnValue({
+ data: {
+ project: {
+ data: {
+ environments: [],
+ },
+ },
+ },
+ });
+
+ return testAction(
+ filterEnvironments,
+ {},
+ state,
+ [
+ {
+ type: 'SET_ENVIRONMENTS_FILTER',
+ payload: {},
+ },
+ ],
+ [
+ {
+ type: 'fetchEnvironmentsData',
+ },
+ ],
+ );
+ });
+
+ it('fetch environments data call takes in search param', () => {
+ const mockMutate = jest.spyOn(gqClient, 'mutate');
+ const searchTerm = 'Something';
+ const mutationVariables = {
+ mutation: getEnvironments,
+ variables: {
+ projectPath: state.projectPath,
+ search: searchTerm,
+ states: [ENVIRONMENT_AVAILABLE_STATE],
+ },
+ };
+ state.environmentsSearchTerm = searchTerm;
+ mockMutate.mockResolvedValue({});
+
+ return testAction(
+ fetchEnvironmentsData,
+ null,
+ state,
+ [],
+ [
+ { type: 'requestEnvironmentsData' },
+ { type: 'receiveEnvironmentsDataSuccess', payload: [] },
+ ],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+
+ it('dispatches receiveEnvironmentsDataSuccess on success', () => {
+ jest.spyOn(gqClient, 'mutate').mockResolvedValue({
+ data: {
+ project: {
+ data: {
+ environments: environmentData,
+ },
+ },
+ },
+ });
+
+ return testAction(
+ fetchEnvironmentsData,
+ null,
+ state,
+ [],
+ [
+ { type: 'requestEnvironmentsData' },
+ {
+ type: 'receiveEnvironmentsDataSuccess',
+ payload: parseEnvironmentsResponse(environmentData, state.projectPath),
+ },
+ ],
+ );
+ });
+
+ it('dispatches receiveEnvironmentsDataFailure on error', () => {
+ jest.spyOn(gqClient, 'mutate').mockRejectedValue({});
+
+ return testAction(
+ fetchEnvironmentsData,
+ null,
+ state,
+ [],
+ [{ type: 'requestEnvironmentsData' }, { type: 'receiveEnvironmentsDataFailure' }],
+ );
+ });
+ });
+
+ describe('fetchAnnotations', () => {
+ beforeEach(() => {
+ state.timeRange = {
+ start: '2020-04-15T12:54:32.137Z',
+ end: '2020-08-15T12:54:32.137Z',
+ };
+ state.projectPath = 'gitlab-org/gitlab-test';
+ state.currentEnvironmentName = 'production';
+ state.currentDashboard = '.gitlab/dashboards/custom_dashboard.yml';
+ // testAction doesn't have access to getters. The state is passed in as getters
+ // instead of the actual getters inside the testAction method implementation.
+ // All methods downstream that needs access to getters will throw and error.
+ // For that reason, the result of the getter is set as a state variable.
+ state.fullDashboardPath = store.getters['monitoringDashboard/fullDashboardPath'];
+ });
+
+ it('fetches annotations data and dispatches receiveAnnotationsSuccess', () => {
+ const mockMutate = jest.spyOn(gqClient, 'mutate');
+ const mutationVariables = {
+ mutation: getAnnotations,
+ variables: {
+ projectPath: state.projectPath,
+ environmentName: state.currentEnvironmentName,
+ dashboardPath: state.currentDashboard,
+ startingFrom: state.timeRange.start,
+ },
+ };
+ const parsedResponse = parseAnnotationsResponse(annotationsData);
+
+ mockMutate.mockResolvedValue({
+ data: {
+ project: {
+ environments: {
+ nodes: [
+ {
+ metricsDashboard: {
+ annotations: {
+ nodes: parsedResponse,
+ },
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ return testAction(
+ fetchAnnotations,
+ null,
+ state,
+ [],
+ [{ type: 'receiveAnnotationsSuccess', payload: parsedResponse }],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+
+ it('dispatches receiveAnnotationsFailure if the annotations API call fails', () => {
+ const mockMutate = jest.spyOn(gqClient, 'mutate');
+ const mutationVariables = {
+ mutation: getAnnotations,
+ variables: {
+ projectPath: state.projectPath,
+ environmentName: state.currentEnvironmentName,
+ dashboardPath: state.currentDashboard,
+ startingFrom: state.timeRange.start,
+ },
+ };
+
+ mockMutate.mockRejectedValue({});
+
+ return testAction(
+ fetchAnnotations,
+ null,
+ state,
+ [],
+ [{ type: 'receiveAnnotationsFailure' }],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+ });
+
+ describe('fetchDashboardValidationWarnings', () => {
+ let mockMutate;
+ let mutationVariables;
+
+ beforeEach(() => {
+ state.projectPath = 'gitlab-org/gitlab-test';
+ state.currentEnvironmentName = 'production';
+ state.currentDashboard = '.gitlab/dashboards/dashboard_with_warnings.yml';
+ // testAction doesn't have access to getters. The state is passed in as getters
+ // instead of the actual getters inside the testAction method implementation.
+ // All methods downstream that needs access to getters will throw and error.
+ // For that reason, the result of the getter is set as a state variable.
+ state.fullDashboardPath = store.getters['monitoringDashboard/fullDashboardPath'];
+
+ mockMutate = jest.spyOn(gqClient, 'mutate');
+ mutationVariables = {
+ mutation: getDashboardValidationWarnings,
+ variables: {
+ projectPath: state.projectPath,
+ environmentName: state.currentEnvironmentName,
+ dashboardPath: state.fullDashboardPath,
+ },
+ };
+ });
+
+ it('dispatches receiveDashboardValidationWarningsSuccess with true payload when there are warnings', () => {
+ mockMutate.mockResolvedValue({
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/29',
+ environments: {
+ nodes: [
+ {
+ name: 'production',
+ metricsDashboard: {
+ path: '.gitlab/dashboards/dashboard_errors_test.yml',
+ schemaValidationWarnings: ["unit: can't be blank"],
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ return testAction(
+ fetchDashboardValidationWarnings,
+ null,
+ state,
+ [],
+ [{ type: 'receiveDashboardValidationWarningsSuccess', payload: true }],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+
+ it('dispatches receiveDashboardValidationWarningsSuccess with false payload when there are no warnings', () => {
+ mockMutate.mockResolvedValue({
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/29',
+ environments: {
+ nodes: [
+ {
+ name: 'production',
+ metricsDashboard: {
+ path: '.gitlab/dashboards/dashboard_errors_test.yml',
+ schemaValidationWarnings: [],
+ },
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ return testAction(
+ fetchDashboardValidationWarnings,
+ null,
+ state,
+ [],
+ [{ type: 'receiveDashboardValidationWarningsSuccess', payload: false }],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+
+ it('dispatches receiveDashboardValidationWarningsSuccess with false payload when the response is empty ', () => {
+ mockMutate.mockResolvedValue({
+ data: {
+ project: null,
+ },
+ });
+
+ return testAction(
+ fetchDashboardValidationWarnings,
+ null,
+ state,
+ [],
+ [{ type: 'receiveDashboardValidationWarningsSuccess', payload: false }],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+
+ it('dispatches receiveDashboardValidationWarningsFailure if the warnings API call fails', () => {
+ mockMutate.mockRejectedValue({});
+
+ return testAction(
+ fetchDashboardValidationWarnings,
+ null,
+ state,
+ [],
+ [{ type: 'receiveDashboardValidationWarningsFailure' }],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+ });
+
+ // Dashboard manipulation
+
+ describe('toggleStarredValue', () => {
+ let unstarredDashboard;
+ let starredDashboard;
+
+ beforeEach(() => {
+ state.isUpdatingStarredValue = false;
+ [unstarredDashboard, starredDashboard] = dashboardGitResponse;
+ });
+
+ it('performs no changes if no dashboard is selected', () => {
+ return testAction(toggleStarredValue, null, state, [], []);
+ });
+
+ it('performs no changes if already changing starred value', () => {
+ state.selectedDashboard = unstarredDashboard;
+ state.isUpdatingStarredValue = true;
+ return testAction(toggleStarredValue, null, state, [], []);
+ });
+
+ it('stars dashboard if it is not starred', () => {
+ state.selectedDashboard = unstarredDashboard;
+ mock.onPost(unstarredDashboard.user_starred_path).reply(200);
+
+ return testAction(toggleStarredValue, null, state, [
+ { type: types.REQUEST_DASHBOARD_STARRING },
+ {
+ type: types.RECEIVE_DASHBOARD_STARRING_SUCCESS,
+ payload: {
+ newStarredValue: true,
+ selectedDashboard: unstarredDashboard,
+ },
+ },
+ ]);
+ });
+
+ it('unstars dashboard if it is starred', () => {
+ state.selectedDashboard = starredDashboard;
+ mock.onPost(starredDashboard.user_starred_path).reply(200);
+
+ return testAction(toggleStarredValue, null, state, [
+ { type: types.REQUEST_DASHBOARD_STARRING },
+ { type: types.RECEIVE_DASHBOARD_STARRING_FAILURE },
+ ]);
+ });
+ });
+
describe('duplicateSystemDashboard', () => {
beforeEach(() => {
state.dashboardsEndpoint = '/dashboards.json';
@@ -979,30 +1114,95 @@ describe('Monitoring store actions', () => {
});
});
- describe('setExpandedPanel', () => {
- it('Sets a panel as expanded', () => {
- const group = 'group_1';
- const panel = { title: 'A Panel' };
+ // Variables manipulation
- return testAction(
- setExpandedPanel,
- { group, panel },
+ describe('updateVariablesAndFetchData', () => {
+ it('should commit UPDATE_VARIABLE_VALUE mutation and fetch data', done => {
+ testAction(
+ updateVariablesAndFetchData,
+ { pod: 'POD' },
state,
- [{ type: types.SET_EXPANDED_PANEL, payload: { group, panel } }],
- [],
+ [
+ {
+ type: types.UPDATE_VARIABLE_VALUE,
+ payload: { pod: 'POD' },
+ },
+ ],
+ [
+ {
+ type: 'fetchDashboardData',
+ },
+ ],
+ done,
);
});
});
- describe('clearExpandedPanel', () => {
- it('Clears a panel as expanded', () => {
+ describe('fetchVariableMetricLabelValues', () => {
+ const variable = {
+ type: 'metric_label_values',
+ name: 'label1',
+ options: {
+ prometheusEndpointPath: '/series?match[]=metric_name',
+ label: 'job',
+ },
+ };
+
+ const defaultQueryParams = {
+ start_time: '2019-08-06T12:40:02.184Z',
+ end_time: '2019-08-06T20:40:02.184Z',
+ };
+
+ beforeEach(() => {
+ state = {
+ ...state,
+ timeRange: defaultTimeRange,
+ variables: [variable],
+ };
+ });
+
+ it('should commit UPDATE_VARIABLE_METRIC_LABEL_VALUES mutation and fetch data', () => {
+ const data = [
+ {
+ __name__: 'up',
+ job: 'prometheus',
+ },
+ {
+ __name__: 'up',
+ job: 'POD',
+ },
+ ];
+
+ mock.onGet('/series?match[]=metric_name').reply(200, {
+ status: 'success',
+ data,
+ });
+
return testAction(
- clearExpandedPanel,
- undefined,
+ fetchVariableMetricLabelValues,
+ { defaultQueryParams },
state,
- [{ type: types.SET_EXPANDED_PANEL, payload: { group: null, panel: null } }],
+ [
+ {
+ type: types.UPDATE_VARIABLE_METRIC_LABEL_VALUES,
+ payload: { variable, label: 'job', data },
+ },
+ ],
[],
);
});
+
+ it('should notify the user that dynamic options were not loaded', () => {
+ mock.onGet('/series?match[]=metric_name').reply(500);
+
+ return testAction(fetchVariableMetricLabelValues, { defaultQueryParams }, state, [], []).then(
+ () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledWith(
+ expect.stringContaining('error getting options for variable "label1"'),
+ );
+ },
+ );
+ });
});
});