diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /spec/frontend/monitoring/store/actions_spec.js | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) | |
download | gitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'spec/frontend/monitoring/store/actions_spec.js')
-rw-r--r-- | spec/frontend/monitoring/store/actions_spec.js | 980 |
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"'), + ); + }, + ); + }); }); }); |