Commit 06edcc35 authored by Miguel Rincon's avatar Miguel Rincon

Reorder specs to match actions implementation

As our store has evolved, both our specs and actions have been
reorganized.

This change allows code authors to see more easily where are specs
located.
parent 10fc6fe7
...@@ -12,21 +12,21 @@ import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants'; ...@@ -12,21 +12,21 @@ import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types'; import * as types from '~/monitoring/stores/mutation_types';
import { import {
setGettingStartedEmptyState,
setInitialState,
setExpandedPanel,
clearExpandedPanel,
filterEnvironments,
fetchData, fetchData,
fetchDashboard, fetchDashboard,
receiveMetricsDashboardSuccess, receiveMetricsDashboardSuccess,
fetchDashboardData,
fetchPrometheusMetric,
fetchDeploymentsData, fetchDeploymentsData,
fetchEnvironmentsData, fetchEnvironmentsData,
fetchDashboardData,
fetchAnnotations, fetchAnnotations,
fetchDashboardValidationWarnings, fetchDashboardValidationWarnings,
toggleStarredValue, toggleStarredValue,
fetchPrometheusMetric,
setInitialState,
filterEnvironments,
setExpandedPanel,
clearExpandedPanel,
setGettingStartedEmptyState,
duplicateSystemDashboard, duplicateSystemDashboard,
updateVariablesAndFetchData, updateVariablesAndFetchData,
} from '~/monitoring/stores/actions'; } from '~/monitoring/stores/actions';
...@@ -81,6 +81,7 @@ describe('Monitoring store actions', () => { ...@@ -81,6 +81,7 @@ describe('Monitoring store actions', () => {
return q; return q;
}); });
}); });
afterEach(() => { afterEach(() => {
mock.reset(); mock.reset();
...@@ -88,665 +89,313 @@ describe('Monitoring store actions', () => { ...@@ -88,665 +89,313 @@ describe('Monitoring store actions', () => {
createFlash.mockReset(); createFlash.mockReset();
}); });
describe('fetchData', () => { // Setup
it('dispatches fetchEnvironmentsData and fetchEnvironmentsData', () => {
return testAction( describe('setGettingStartedEmptyState', () => {
fetchData, it('should commit SET_GETTING_STARTED_EMPTY_STATE mutation', done => {
testAction(
setGettingStartedEmptyState,
null, null,
state, state,
[],
[ [
{ type: 'fetchEnvironmentsData' }, {
{ type: 'fetchDashboard' }, type: types.SET_GETTING_STARTED_EMPTY_STATE,
{ type: 'fetchAnnotations' }, },
], ],
[],
done,
); );
}); });
});
it('dispatches when feature metricsDashboardAnnotations is on', () => { describe('setInitialState', () => {
const origGon = window.gon; it('should commit SET_INITIAL_STATE mutation', done => {
window.gon = { features: { metricsDashboardAnnotations: true } }; testAction(
setInitialState,
return testAction( {
fetchData, currentDashboard: '.gitlab/dashboards/dashboard.yml',
null, deploymentsEndpoint: 'deployments.json',
},
state, state,
[],
[ [
{ type: 'fetchEnvironmentsData' }, {
{ type: 'fetchDashboard' }, type: types.SET_INITIAL_STATE,
{ type: 'fetchAnnotations' }, payload: {
currentDashboard: '.gitlab/dashboards/dashboard.yml',
deploymentsEndpoint: 'deployments.json',
},
},
], ],
).then(() => { [],
window.gon = origGon; done,
}); );
}); });
}); });
describe('fetchDeploymentsData', () => { describe('setExpandedPanel', () => {
it('dispatches receiveDeploymentsDataSuccess on success', () => { it('Sets a panel as expanded', () => {
state.deploymentsEndpoint = '/success'; const group = 'group_1';
mock.onGet(state.deploymentsEndpoint).reply(200, { const panel = { title: 'A Panel' };
deployments: deploymentData,
});
return testAction( return testAction(
fetchDeploymentsData, setExpandedPanel,
null, { group, panel },
state, state,
[{ type: types.SET_EXPANDED_PANEL, payload: { group, panel } }],
[], [],
[{ type: 'receiveDeploymentsDataSuccess', payload: deploymentData }],
); );
}); });
it('dispatches receiveDeploymentsDataFailure on error', () => { });
state.deploymentsEndpoint = '/error';
mock.onGet(state.deploymentsEndpoint).reply(500);
describe('clearExpandedPanel', () => {
it('Clears a panel as expanded', () => {
return testAction( return testAction(
fetchDeploymentsData, clearExpandedPanel,
null, undefined,
state, state,
[{ type: types.SET_EXPANDED_PANEL, payload: { group: null, panel: null } }],
[], [],
[{ type: 'receiveDeploymentsDataFailure' }],
() => {
expect(createFlash).toHaveBeenCalled();
},
); );
}); });
}); });
describe('fetchEnvironmentsData', () => { // All Data
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('fetchData', () => {
it('dispatches fetchEnvironmentsData and fetchEnvironmentsData', () => {
return testAction( return testAction(
filterEnvironments, fetchData,
{}, null,
state, state,
[],
[ [
{ { type: 'fetchEnvironmentsData' },
type: 'SET_ENVIRONMENTS_FILTER', { type: 'fetchDashboard' },
payload: {}, { type: 'fetchAnnotations' },
},
],
[
{
type: 'fetchEnvironmentsData',
},
], ],
); );
}); });
it('fetch environments data call takes in search param', () => { it('dispatches when feature metricsDashboardAnnotations is on', () => {
const mockMutate = jest.spyOn(gqClient, 'mutate'); const origGon = window.gon;
const searchTerm = 'Something'; window.gon = { features: { metricsDashboardAnnotations: true } };
const mutationVariables = {
mutation: getEnvironments,
variables: {
projectPath: state.projectPath,
search: searchTerm,
states: [ENVIRONMENT_AVAILABLE_STATE],
},
};
state.environmentsSearchTerm = searchTerm;
mockMutate.mockResolvedValue({});
return testAction( return testAction(
fetchEnvironmentsData, fetchData,
null, null,
state, state,
[], [],
[ [
{ type: 'requestEnvironmentsData' }, { type: 'fetchEnvironmentsData' },
{ type: 'receiveEnvironmentsDataSuccess', payload: [] }, { type: 'fetchDashboard' },
{ type: 'fetchAnnotations' },
], ],
() => { ).then(() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables); window.gon = origGon;
}, });
);
}); });
});
it('dispatches receiveEnvironmentsDataSuccess on success', () => { // Metrics dashboard
jest.spyOn(gqClient, 'mutate').mockResolvedValue({
data: { describe('fetchDashboard', () => {
project: { let dispatch;
data: { let commit;
environments: environmentData, const response = metricsDashboardResponse;
}, beforeEach(() => {
}, dispatch = jest.fn();
}, commit = jest.fn();
}); state.dashboardEndpoint = '/dashboard';
});
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);
return testAction( return testAction(
fetchEnvironmentsData, fetchDashboard,
null, null,
state, state,
[], [],
[ [
{ type: 'requestEnvironmentsData' }, { type: 'requestMetricsDashboard' },
{ {
type: 'receiveEnvironmentsDataSuccess', type: 'receiveMetricsDashboardSuccess',
payload: parseEnvironmentsResponse(environmentData, state.projectPath), payload: { response },
}, },
{ type: 'fetchDashboardValidationWarnings' },
], ],
); );
}); });
it('dispatches receiveEnvironmentsDataFailure on error', () => { describe('on failure', () => {
jest.spyOn(gqClient, 'mutate').mockRejectedValue({}); let result;
beforeEach(() => {
return testAction( const params = {};
fetchEnvironmentsData, const localGetters = {
null, fullDashboardPath: store.getters['monitoringDashboard/fullDashboardPath'],
state, };
[], result = () => {
[{ type: 'requestEnvironmentsData' }, { type: 'receiveEnvironmentsDataFailure' }], mock.onGet(state.dashboardEndpoint).replyOnce(500, mockDashboardsErrorResponse);
); return fetchDashboard({ state, commit, dispatch, getters: localGetters }, params);
}); };
}); });
describe('fetchAnnotations', () => { it('dispatches a failure', done => {
beforeEach(() => { result()
state.timeRange = { .then(() => {
start: '2020-04-15T12:54:32.137Z', expect(commit).toHaveBeenCalledWith(
end: '2020-08-15T12:54:32.137Z', types.SET_ALL_DASHBOARDS,
}; mockDashboardsErrorResponse.all_dashboards,
state.projectPath = 'gitlab-org/gitlab-test'; );
state.currentEnvironmentName = 'production'; expect(dispatch).toHaveBeenCalledWith(
state.currentDashboard = '.gitlab/dashboards/custom_dashboard.yml'; 'receiveMetricsDashboardFailure',
// testAction doesn't have access to getters. The state is passed in as getters new Error('Request failed with status code 500'),
// instead of the actual getters inside the testAction method implementation. );
// All methods downstream that needs access to getters will throw and error. expect(createFlash).toHaveBeenCalled();
// For that reason, the result of the getter is set as a state variable. done();
state.fullDashboardPath = store.getters['monitoringDashboard/fullDashboardPath']; })
}); .catch(done.fail);
});
it('fetches annotations data and dispatches receiveAnnotationsSuccess', () => { it('dispatches a failure action when a message is returned', done => {
const mockMutate = jest.spyOn(gqClient, 'mutate'); result()
const mutationVariables = { .then(() => {
mutation: getAnnotations, expect(dispatch).toHaveBeenCalledWith(
variables: { 'receiveMetricsDashboardFailure',
projectPath: state.projectPath, new Error('Request failed with status code 500'),
environmentName: state.currentEnvironmentName, );
dashboardPath: state.currentDashboard, expect(createFlash).toHaveBeenCalledWith(
startingFrom: state.timeRange.start, expect.stringContaining(mockDashboardsErrorResponse.message),
}, );
}; done();
const parsedResponse = parseAnnotationsResponse(annotationsData); })
.catch(done.fail);
mockMutate.mockResolvedValue({
data: {
project: {
environments: {
nodes: [
{
metricsDashboard: {
annotations: {
nodes: parsedResponse,
},
},
},
],
},
},
},
}); });
return testAction( it('does not show a flash error when showErrorBanner is disabled', done => {
fetchAnnotations, state.showErrorBanner = false;
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( result()
fetchAnnotations, .then(() => {
null, expect(dispatch).toHaveBeenCalledWith(
state, 'receiveMetricsDashboardFailure',
[], new Error('Request failed with status code 500'),
[{ type: 'receiveAnnotationsFailure' }], );
() => { expect(createFlash).not.toHaveBeenCalled();
expect(mockMutate).toHaveBeenCalledWith(mutationVariables); done();
}, })
); .catch(done.fail);
});
}); });
}); });
describe('fetchDashboardValidationWarnings', () => { describe('receiveMetricsDashboardSuccess', () => {
let mockMutate; let commit;
let mutationVariables; let dispatch;
beforeEach(() => { beforeEach(() => {
state.projectPath = 'gitlab-org/gitlab-test'; commit = jest.fn();
state.currentEnvironmentName = 'production'; dispatch = jest.fn();
state.currentDashboard = '.gitlab/dashboards/dashboard_with_warnings.yml';
mockMutate = jest.spyOn(gqClient, 'mutate');
mutationVariables = {
mutation: getDashboardValidationWarnings,
variables: {
projectPath: state.projectPath,
environmentName: state.currentEnvironmentName,
dashboardPath: state.currentDashboard,
},
};
}); });
it('dispatches receiveDashboardValidationWarningsSuccess with true payload when there are warnings', () => { it('stores groups', () => {
mockMutate.mockResolvedValue({ const response = metricsDashboardResponse;
data: { receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response });
project: { expect(commit).toHaveBeenCalledWith(
id: 'gid://gitlab/Project/29', types.RECEIVE_METRICS_DASHBOARD_SUCCESS,
environments: {
nodes: [
{
name: 'production',
metricsDashboard: {
path: '.gitlab/dashboards/dashboard_errors_test.yml',
schemaValidationWarnings: ["unit: can't be blank"],
},
},
],
},
},
},
});
return testAction( metricsDashboardResponse.dashboard,
fetchDashboardValidationWarnings,
null,
state,
[],
[{ type: 'receiveDashboardValidationWarningsSuccess', payload: true }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
); );
expect(dispatch).toHaveBeenCalledWith('fetchDashboardData');
}); });
it('dispatches receiveDashboardValidationWarningsSuccess with false payload when there are no warnings', () => { it('stores templating variables', () => {
mockMutate.mockResolvedValue({ const response = {
data: { ...metricsDashboardResponse.dashboard,
project: { ...mockTemplatingData.allVariableTypes.dashboard,
id: 'gid://gitlab/Project/29', };
environments: {
nodes: [ receiveMetricsDashboardSuccess(
{ { state, commit, dispatch },
name: 'production', {
metricsDashboard: { response: {
path: '.gitlab/dashboards/dashboard_errors_test.yml', ...metricsDashboardResponse,
schemaValidationWarnings: [], dashboard: {
}, ...metricsDashboardResponse.dashboard,
}, ...mockTemplatingData.allVariableTypes.dashboard,
],
}, },
}, },
}, },
}); );
return testAction( expect(commit).toHaveBeenCalledWith(
fetchDashboardValidationWarnings, types.RECEIVE_METRICS_DASHBOARD_SUCCESS,
null,
state, response,
[],
[{ type: 'receiveDashboardValidationWarningsSuccess', payload: false }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
); );
}); });
it('dispatches receiveDashboardValidationWarningsFailure if the warnings API call fails', () => { it('sets the dashboards loaded from the repository', () => {
mockMutate.mockRejectedValue({}); const params = {};
const response = metricsDashboardResponse;
return testAction( response.all_dashboards = dashboardGitResponse;
fetchDashboardValidationWarnings, receiveMetricsDashboardSuccess(
null, {
state, state,
[], commit,
[{ type: 'receiveDashboardValidationWarningsFailure' }], dispatch,
() => { },
expect(mockMutate).toHaveBeenCalledWith(mutationVariables); {
response,
params,
}, },
); );
expect(commit).toHaveBeenCalledWith(types.SET_ALL_DASHBOARDS, dashboardGitResponse);
}); });
}); });
describe('Toggles starred value of current dashboard', () => { // Metrics
let unstarredDashboard;
let starredDashboard;
beforeEach(() => {
state.isUpdatingStarredValue = false;
[unstarredDashboard, starredDashboard] = dashboardGitResponse;
});
describe('toggleStarredValue', () => { describe('fetchDashboardData', () => {
it('performs no changes if no dashboard is selected', () => { let commit;
return testAction(toggleStarredValue, null, state, [], []); let dispatch;
});
it('performs no changes if already changing starred value', () => { beforeEach(() => {
state.selectedDashboard = unstarredDashboard; jest.spyOn(Tracking, 'event');
state.isUpdatingStarredValue = true; commit = jest.fn();
return testAction(toggleStarredValue, null, state, [], []); dispatch = jest.fn();
});
it('stars dashboard if it is not starred', () => { state.timeRange = defaultTimeRange;
state.selectedDashboard = unstarredDashboard; });
mock.onPost(unstarredDashboard.user_starred_path).reply(200);
return testAction(toggleStarredValue, null, state, [ it('commits empty state when state.groups is empty', done => {
{ type: types.REQUEST_DASHBOARD_STARRING }, const localGetters = {
{ metricsWithData: () => [],
type: types.RECEIVE_DASHBOARD_STARRING_SUCCESS, };
payload: { fetchDashboardData({ state, commit, dispatch, getters: localGetters })
newStarredValue: true, .then(() => {
selectedDashboard: unstarredDashboard, expect(Tracking.event).toHaveBeenCalledWith(
document.body.dataset.page,
'dashboard_fetch',
{
label: 'custom_metrics_dashboard',
property: 'count',
value: 0,
}, },
}, );
]); expect(dispatch).toHaveBeenCalledTimes(1);
}); expect(dispatch).toHaveBeenCalledWith('fetchDeploymentsData');
it('unstars dashboard if it is starred', () => {
state.selectedDashboard = starredDashboard;
mock.onPost(starredDashboard.user_starred_path).reply(200);
return testAction(toggleStarredValue, null, state, [ expect(createFlash).not.toHaveBeenCalled();
{ type: types.REQUEST_DASHBOARD_STARRING }, done();
{ type: types.RECEIVE_DASHBOARD_STARRING_FAILURE }, })
]); .catch(done.fail);
});
}); });
}); it('dispatches fetchPrometheusMetric for each panel query', done => {
state.dashboard.panelGroups = convertObjectPropsToCamelCase(
describe('Set initial state', () => { metricsDashboardResponse.dashboard.panel_groups,
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,
},
],
[],
done,
);
});
});
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,
);
});
});
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, then fetches dashboard warnings', () => {
document.body.dataset.page = 'projects:environments:metrics';
mock.onGet(state.dashboardEndpoint).reply(200, response);
return testAction(
fetchDashboard,
null,
state,
[],
[
{ type: 'requestMetricsDashboard' },
{
type: 'receiveMetricsDashboardSuccess',
payload: { response },
},
{ type: 'fetchDashboardValidationWarnings' },
],
);
});
describe('on failure', () => {
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, getters: localGetters }, params);
};
});
it('dispatches a failure', done => {
result()
.then(() => {
expect(commit).toHaveBeenCalledWith(
types.SET_ALL_DASHBOARDS,
mockDashboardsErrorResponse.all_dashboards,
);
expect(dispatch).toHaveBeenCalledWith(
'receiveMetricsDashboardFailure',
new Error('Request failed with status code 500'),
);
expect(createFlash).toHaveBeenCalled();
done();
})
.catch(done.fail);
});
it('dispatches a failure action when a message is returned', done => {
result()
.then(() => {
expect(dispatch).toHaveBeenCalledWith(
'receiveMetricsDashboardFailure',
new Error('Request failed with status code 500'),
);
expect(createFlash).toHaveBeenCalledWith(
expect.stringContaining(mockDashboardsErrorResponse.message),
);
done();
})
.catch(done.fail);
});
it('does not show a flash error when showErrorBanner is disabled', done => {
state.showErrorBanner = false;
result()
.then(() => {
expect(dispatch).toHaveBeenCalledWith(
'receiveMetricsDashboardFailure',
new Error('Request failed with status code 500'),
);
expect(createFlash).not.toHaveBeenCalled();
done();
})
.catch(done.fail);
});
});
});
describe('receiveMetricsDashboardSuccess', () => {
let commit;
let dispatch;
beforeEach(() => {
commit = jest.fn();
dispatch = jest.fn();
});
it('stores groups', () => {
const response = metricsDashboardResponse;
receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response });
expect(commit).toHaveBeenCalledWith(
types.RECEIVE_METRICS_DASHBOARD_SUCCESS,
metricsDashboardResponse.dashboard,
);
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;
response.all_dashboards = dashboardGitResponse;
receiveMetricsDashboardSuccess(
{
state,
commit,
dispatch,
},
{
response,
params,
},
);
expect(commit).toHaveBeenCalledWith(types.SET_ALL_DASHBOARDS, dashboardGitResponse);
});
});
describe('fetchDashboardData', () => {
let commit;
let dispatch;
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 localGetters = {
metricsWithData: () => [],
};
fetchDashboardData({ state, commit, dispatch, getters: localGetters })
.then(() => {
expect(Tracking.event).toHaveBeenCalledWith(
document.body.dataset.page,
'dashboard_fetch',
{
label: 'custom_metrics_dashboard',
property: 'count',
value: 0,
},
);
expect(dispatch).toHaveBeenCalledTimes(1);
expect(dispatch).toHaveBeenCalledWith('fetchDeploymentsData');
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 [metric] = state.dashboard.panelGroups[0].panels[0].metrics;
const localGetters = { const localGetters = {
...@@ -810,6 +459,7 @@ describe('Monitoring store actions', () => { ...@@ -810,6 +459,7 @@ describe('Monitoring store actions', () => {
done(); done();
}); });
}); });
describe('fetchPrometheusMetric', () => { describe('fetchPrometheusMetric', () => {
const defaultQueryParams = { const defaultQueryParams = {
start_time: '2019-08-06T12:40:02.184Z', start_time: '2019-08-06T12:40:02.184Z',
...@@ -826,190 +476,562 @@ describe('Monitoring store actions', () => { ...@@ -826,190 +476,562 @@ describe('Monitoring store actions', () => {
prometheusEndpointPath = metric.prometheusEndpointPath; prometheusEndpointPath = metric.prometheusEndpointPath;
data = { data = {
metricId: metric.metricId, metricId: metric.metricId,
result: [1582065167.353, 5, 1582065599.353], result: [1582065167.353, 5, 1582065599.353],
}; };
});
it('commits result', done => {
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
testAction(
fetchPrometheusMetric,
{ metric, defaultQueryParams },
state,
[
{
type: types.REQUEST_METRIC_RESULT,
payload: {
metricId: metric.metricId,
},
},
{
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
},
},
],
[],
() => {
expect(mock.history.get).toHaveLength(1);
done();
},
).catch(done.fail);
});
describe('without metric defined step', () => {
const expectedParams = {
start_time: '2019-08-06T12:40:02.184Z',
end_time: '2019-08-06T20:40:02.184Z',
step: 60,
};
it('uses calculated step', done => {
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
testAction(
fetchPrometheusMetric,
{ metric, defaultQueryParams },
state,
[
{
type: types.REQUEST_METRIC_RESULT,
payload: {
metricId: metric.metricId,
},
},
{
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
},
},
],
[],
() => {
expect(mock.history.get[0].params).toEqual(expectedParams);
done();
},
).catch(done.fail);
});
});
describe('with metric defined step', () => {
beforeEach(() => {
metric.step = 7;
});
const expectedParams = {
start_time: '2019-08-06T12:40:02.184Z',
end_time: '2019-08-06T20:40:02.184Z',
step: 7,
};
it('uses metric step', done => {
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
testAction(
fetchPrometheusMetric,
{ metric, defaultQueryParams },
state,
[
{
type: types.REQUEST_METRIC_RESULT,
payload: {
metricId: metric.metricId,
},
},
{
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
},
},
],
[],
() => {
expect(mock.history.get[0].params).toEqual(expectedParams);
done();
},
).catch(done.fail);
});
});
it('commits result, when waiting for results', done => {
// Mock multiple attempts while the cache is filling up
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).reply(200, { data }); // 4th attempt
testAction(
fetchPrometheusMetric,
{ metric, defaultQueryParams },
state,
[
{
type: types.REQUEST_METRIC_RESULT,
payload: {
metricId: metric.metricId,
},
},
{
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
},
},
],
[],
() => {
expect(mock.history.get).toHaveLength(4);
done();
},
).catch(done.fail);
});
it('commits failure, when waiting for results and getting a server error', done => {
// Mock multiple attempts while the cache is filling up and fails
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).reply(500); // 4th attempt
const error = new Error('Request failed with status code 500');
testAction(
fetchPrometheusMetric,
{ metric, defaultQueryParams },
state,
[
{
type: types.REQUEST_METRIC_RESULT,
payload: {
metricId: metric.metricId,
},
},
{
type: types.RECEIVE_METRIC_RESULT_FAILURE,
payload: {
metricId: metric.metricId,
error,
},
},
],
[],
).catch(e => {
expect(mock.history.get).toHaveLength(4);
expect(e).toEqual(error);
done();
});
});
});
// 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';
mockMutate = jest.spyOn(gqClient, 'mutate');
mutationVariables = {
mutation: getDashboardValidationWarnings,
variables: {
projectPath: state.projectPath,
environmentName: state.currentEnvironmentName,
dashboardPath: state.currentDashboard,
},
};
});
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('commits result', done => { it('dispatches receiveDashboardValidationWarningsSuccess with false payload when there are no warnings', () => {
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt mockMutate.mockResolvedValue({
data: {
testAction( project: {
fetchPrometheusMetric, id: 'gid://gitlab/Project/29',
{ metric, defaultQueryParams }, environments: {
state, nodes: [
[ {
{ name: 'production',
type: types.REQUEST_METRIC_RESULT, metricsDashboard: {
payload: { path: '.gitlab/dashboards/dashboard_errors_test.yml',
metricId: metric.metricId, schemaValidationWarnings: [],
}, },
}, },
{ ],
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
}, },
}, },
], },
});
return testAction(
fetchDashboardValidationWarnings,
null,
state,
[], [],
[{ type: 'receiveDashboardValidationWarningsSuccess', payload: false }],
() => { () => {
expect(mock.history.get).toHaveLength(1); expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
done();
}, },
).catch(done.fail); );
}); });
describe('without metric defined step', () => { it('dispatches receiveDashboardValidationWarningsFailure if the warnings API call fails', () => {
const expectedParams = { mockMutate.mockRejectedValue({});
start_time: '2019-08-06T12:40:02.184Z',
end_time: '2019-08-06T20:40:02.184Z',
step: 60,
};
it('uses calculated step', done => {
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
testAction( return testAction(
fetchPrometheusMetric, fetchDashboardValidationWarnings,
{ metric, defaultQueryParams }, null,
state, state,
[ [],
{ [{ type: 'receiveDashboardValidationWarningsFailure' }],
type: types.REQUEST_METRIC_RESULT, () => {
payload: { expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
metricId: metric.metricId, },
}, );
},
{
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
},
},
],
[],
() => {
expect(mock.history.get[0].params).toEqual(expectedParams);
done();
},
).catch(done.fail);
});
}); });
});
describe('with metric defined step', () => { // Dashboard manipulation
beforeEach(() => {
metric.step = 7;
});
const expectedParams = { describe('toggleStarredValue', () => {
start_time: '2019-08-06T12:40:02.184Z', let unstarredDashboard;
end_time: '2019-08-06T20:40:02.184Z', let starredDashboard;
step: 7,
};
it('uses metric step', done => { beforeEach(() => {
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt state.isUpdatingStarredValue = false;
[unstarredDashboard, starredDashboard] = dashboardGitResponse;
});
testAction( it('performs no changes if no dashboard is selected', () => {
fetchPrometheusMetric, return testAction(toggleStarredValue, null, state, [], []);
{ metric, defaultQueryParams },
state,
[
{
type: types.REQUEST_METRIC_RESULT,
payload: {
metricId: metric.metricId,
},
},
{
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
},
},
],
[],
() => {
expect(mock.history.get[0].params).toEqual(expectedParams);
done();
},
).catch(done.fail);
});
}); });
it('commits result, when waiting for results', done => { it('performs no changes if already changing starred value', () => {
// Mock multiple attempts while the cache is filling up state.selectedDashboard = unstarredDashboard;
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT); state.isUpdatingStarredValue = true;
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT); return testAction(toggleStarredValue, null, state, [], []);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT); });
mock.onGet(prometheusEndpointPath).reply(200, { data }); // 4th attempt
testAction( it('stars dashboard if it is not starred', () => {
fetchPrometheusMetric, state.selectedDashboard = unstarredDashboard;
{ metric, defaultQueryParams }, mock.onPost(unstarredDashboard.user_starred_path).reply(200);
state,
[ return testAction(toggleStarredValue, null, state, [
{ { type: types.REQUEST_DASHBOARD_STARRING },
type: types.REQUEST_METRIC_RESULT, {
payload: { type: types.RECEIVE_DASHBOARD_STARRING_SUCCESS,
metricId: metric.metricId, payload: {
}, newStarredValue: true,
}, selectedDashboard: unstarredDashboard,
{
type: types.RECEIVE_METRIC_RESULT_SUCCESS,
payload: {
metricId: metric.metricId,
data,
},
}, },
],
[],
() => {
expect(mock.history.get).toHaveLength(4);
done();
}, },
).catch(done.fail); ]);
}); });
it('commits failure, when waiting for results and getting a server error', done => { it('unstars dashboard if it is starred', () => {
// Mock multiple attempts while the cache is filling up and fails state.selectedDashboard = starredDashboard;
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT); mock.onPost(starredDashboard.user_starred_path).reply(200);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).reply(500); // 4th attempt
const error = new Error('Request failed with status code 500');
testAction( return testAction(toggleStarredValue, null, state, [
fetchPrometheusMetric, { type: types.REQUEST_DASHBOARD_STARRING },
{ metric, defaultQueryParams }, { type: types.RECEIVE_DASHBOARD_STARRING_FAILURE },
state, ]);
[
{
type: types.REQUEST_METRIC_RESULT,
payload: {
metricId: metric.metricId,
},
},
{
type: types.RECEIVE_METRIC_RESULT_FAILURE,
payload: {
metricId: metric.metricId,
error,
},
},
],
[],
).catch(e => {
expect(mock.history.get).toHaveLength(4);
expect(e).toEqual(error);
done();
});
}); });
}); });
...@@ -1091,29 +1113,26 @@ describe('Monitoring store actions', () => { ...@@ -1091,29 +1113,26 @@ describe('Monitoring store actions', () => {
}); });
}); });
describe('setExpandedPanel', () => { // Variables manipulation
it('Sets a panel as expanded', () => {
const group = 'group_1';
const panel = { title: 'A Panel' };
return testAction(
setExpandedPanel,
{ group, panel },
state,
[{ type: types.SET_EXPANDED_PANEL, payload: { group, panel } }],
[],
);
});
});
describe('clearExpandedPanel', () => { describe('updateVariablesAndFetchData', () => {
it('Clears a panel as expanded', () => { it('should commit UPDATE_VARIABLES mutation and fetch data', done => {
return testAction( testAction(
clearExpandedPanel, updateVariablesAndFetchData,
undefined, { pod: 'POD' },
state, state,
[{ type: types.SET_EXPANDED_PANEL, payload: { group: null, panel: null } }], [
[], {
type: types.UPDATE_VARIABLES,
payload: { pod: 'POD' },
},
],
[
{
type: 'fetchDashboardData',
},
],
done,
); );
}); });
}); });
......
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