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';
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,
} from '~/monitoring/stores/actions';
......@@ -81,6 +81,7 @@ describe('Monitoring store actions', () => {
return q;
});
});
afterEach(() => {
mock.reset();
......@@ -88,665 +89,313 @@ describe('Monitoring store actions', () => {
createFlash.mockReset();
});
describe('fetchData', () => {
it('dispatches fetchEnvironmentsData and fetchEnvironmentsData', () => {
return testAction(
fetchData,
// Setup
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,
},
],
[],
done,
);
});
});
it('dispatches when feature metricsDashboardAnnotations is on', () => {
const origGon = window.gon;
window.gon = { features: { metricsDashboardAnnotations: true } };
return testAction(
fetchData,
null,
describe('setInitialState', () => {
it('should commit SET_INITIAL_STATE mutation', done => {
testAction(
setInitialState,
{
currentDashboard: '.gitlab/dashboards/dashboard.yml',
deploymentsEndpoint: 'deployments.json',
},
state,
[],
[
{ type: 'fetchEnvironmentsData' },
{ type: 'fetchDashboard' },
{ type: 'fetchAnnotations' },
{
type: types.SET_INITIAL_STATE,
payload: {
currentDashboard: '.gitlab/dashboards/dashboard.yml',
deploymentsEndpoint: 'deployments.json',
},
},
],
).then(() => {
window.gon = origGon;
});
[],
done,
);
});
});
describe('fetchDeploymentsData', () => {
it('dispatches receiveDeploymentsDataSuccess on success', () => {
state.deploymentsEndpoint = '/success';
mock.onGet(state.deploymentsEndpoint).reply(200, {
deployments: deploymentData,
});
describe('setExpandedPanel', () => {
it('Sets a panel as expanded', () => {
const group = 'group_1';
const panel = { title: 'A Panel' };
return testAction(
fetchDeploymentsData,
null,
setExpandedPanel,
{ group, panel },
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(
fetchDeploymentsData,
null,
clearExpandedPanel,
undefined,
state,
[{ type: types.SET_EXPANDED_PANEL, payload: { group: null, panel: null } }],
[],
[{ type: 'receiveDeploymentsDataFailure' }],
() => {
expect(createFlash).toHaveBeenCalled();
},
);
});
});
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: [],
},
},
},
});
// All Data
describe('fetchData', () => {
it('dispatches fetchEnvironmentsData and fetchEnvironmentsData', () => {
return testAction(
filterEnvironments,
{},
fetchData,
null,
state,
[],
[
{
type: 'SET_ENVIRONMENTS_FILTER',
payload: {},
},
],
[
{
type: 'fetchEnvironmentsData',
},
{ type: 'fetchEnvironmentsData' },
{ type: 'fetchDashboard' },
{ type: 'fetchAnnotations' },
],
);
});
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({});
it('dispatches when feature metricsDashboardAnnotations is on', () => {
const origGon = window.gon;
window.gon = { features: { metricsDashboardAnnotations: true } };
return testAction(
fetchEnvironmentsData,
fetchData,
null,
state,
[],
[
{ type: 'requestEnvironmentsData' },
{ type: 'receiveEnvironmentsDataSuccess', payload: [] },
{ type: 'fetchEnvironmentsData' },
{ type: 'fetchDashboard' },
{ type: 'fetchAnnotations' },
],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
);
).then(() => {
window.gon = origGon;
});
});
});
it('dispatches receiveEnvironmentsDataSuccess on success', () => {
jest.spyOn(gqClient, 'mutate').mockResolvedValue({
data: {
project: {
data: {
environments: environmentData,
},
},
},
});
// 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, then fetches dashboard warnings', () => {
document.body.dataset.page = 'projects:environments:metrics';
mock.onGet(state.dashboardEndpoint).reply(200, response);
return testAction(
fetchEnvironmentsData,
fetchDashboard,
null,
state,
[],
[
{ type: 'requestEnvironmentsData' },
{ type: 'requestMetricsDashboard' },
{
type: 'receiveEnvironmentsDataSuccess',
payload: parseEnvironmentsResponse(environmentData, state.projectPath),
type: 'receiveMetricsDashboardSuccess',
payload: { response },
},
{ type: 'fetchDashboardValidationWarnings' },
],
);
});
it('dispatches receiveEnvironmentsDataFailure on error', () => {
jest.spyOn(gqClient, 'mutate').mockRejectedValue({});
return testAction(
fetchEnvironmentsData,
null,
state,
[],
[{ type: 'requestEnvironmentsData' }, { type: 'receiveEnvironmentsDataFailure' }],
);
});
});
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);
};
});
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('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('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,
},
},
},
],
},
},
},
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);
});
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({});
it('does not show a flash error when showErrorBanner is disabled', done => {
state.showErrorBanner = false;
return testAction(
fetchAnnotations,
null,
state,
[],
[{ type: 'receiveAnnotationsFailure' }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
);
result()
.then(() => {
expect(dispatch).toHaveBeenCalledWith(
'receiveMetricsDashboardFailure',
new Error('Request failed with status code 500'),
);
expect(createFlash).not.toHaveBeenCalled();
done();
})
.catch(done.fail);
});
});
});
describe('fetchDashboardValidationWarnings', () => {
let mockMutate;
let mutationVariables;
describe('receiveMetricsDashboardSuccess', () => {
let commit;
let dispatch;
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,
},
};
commit = jest.fn();
dispatch = jest.fn();
});
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"],
},
},
],
},
},
},
});
it('stores groups', () => {
const response = metricsDashboardResponse;
receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response });
expect(commit).toHaveBeenCalledWith(
types.RECEIVE_METRICS_DASHBOARD_SUCCESS,
return testAction(
fetchDashboardValidationWarnings,
null,
state,
[],
[{ type: 'receiveDashboardValidationWarningsSuccess', payload: true }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
metricsDashboardResponse.dashboard,
);
expect(dispatch).toHaveBeenCalledWith('fetchDashboardData');
});
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: [],
},
},
],
it('stores templating variables', () => {
const response = {
...metricsDashboardResponse.dashboard,
...mockTemplatingData.allVariableTypes.dashboard,
};
receiveMetricsDashboardSuccess(
{ state, commit, dispatch },
{
response: {
...metricsDashboardResponse,
dashboard: {
...metricsDashboardResponse.dashboard,
...mockTemplatingData.allVariableTypes.dashboard,
},
},
},
});
);
return testAction(
fetchDashboardValidationWarnings,
null,
state,
[],
[{ type: 'receiveDashboardValidationWarningsSuccess', payload: false }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
expect(commit).toHaveBeenCalledWith(
types.RECEIVE_METRICS_DASHBOARD_SUCCESS,
response,
);
});
it('dispatches receiveDashboardValidationWarningsFailure if the warnings API call fails', () => {
mockMutate.mockRejectedValue({});
return testAction(
fetchDashboardValidationWarnings,
null,
state,
[],
[{ type: 'receiveDashboardValidationWarningsFailure' }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
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('Toggles starred value of current dashboard', () => {
let unstarredDashboard;
let starredDashboard;
beforeEach(() => {
state.isUpdatingStarredValue = false;
[unstarredDashboard, starredDashboard] = dashboardGitResponse;
});
// Metrics
describe('toggleStarredValue', () => {
it('performs no changes if no dashboard is selected', () => {
return testAction(toggleStarredValue, null, state, [], []);
});
describe('fetchDashboardData', () => {
let commit;
let dispatch;
it('performs no changes if already changing starred value', () => {
state.selectedDashboard = unstarredDashboard;
state.isUpdatingStarredValue = true;
return testAction(toggleStarredValue, null, state, [], []);
});
beforeEach(() => {
jest.spyOn(Tracking, 'event');
commit = jest.fn();
dispatch = jest.fn();
it('stars dashboard if it is not starred', () => {
state.selectedDashboard = unstarredDashboard;
mock.onPost(unstarredDashboard.user_starred_path).reply(200);
state.timeRange = defaultTimeRange;
});
return testAction(toggleStarredValue, null, state, [
{ type: types.REQUEST_DASHBOARD_STARRING },
{
type: types.RECEIVE_DASHBOARD_STARRING_SUCCESS,
payload: {
newStarredValue: true,
selectedDashboard: unstarredDashboard,
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,
},
},
]);
});
it('unstars dashboard if it is starred', () => {
state.selectedDashboard = starredDashboard;
mock.onPost(starredDashboard.user_starred_path).reply(200);
);
expect(dispatch).toHaveBeenCalledTimes(1);
expect(dispatch).toHaveBeenCalledWith('fetchDeploymentsData');
return testAction(toggleStarredValue, null, state, [
{ type: types.REQUEST_DASHBOARD_STARRING },
{ type: types.RECEIVE_DASHBOARD_STARRING_FAILURE },
]);
});
expect(createFlash).not.toHaveBeenCalled();
done();
})
.catch(done.fail);
});
});
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,
},
],
[],
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,
);
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 localGetters = {
......@@ -810,6 +459,7 @@ describe('Monitoring store actions', () => {
done();
});
});
describe('fetchPrometheusMetric', () => {
const defaultQueryParams = {
start_time: '2019-08-06T12:40:02.184Z',
......@@ -826,190 +476,562 @@ describe('Monitoring store actions', () => {
prometheusEndpointPath = metric.prometheusEndpointPath;
data = {
metricId: metric.metricId,
result: [1582065167.353, 5, 1582065599.353],
};
data = {
metricId: metric.metricId,
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 => {
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,
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(mock.history.get).toHaveLength(1);
done();
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
).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
it('dispatches receiveDashboardValidationWarningsFailure if the warnings API call fails', () => {
mockMutate.mockRejectedValue({});
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);
});
return testAction(
fetchDashboardValidationWarnings,
null,
state,
[],
[{ type: 'receiveDashboardValidationWarningsFailure' }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
);
});
});
describe('with metric defined step', () => {
beforeEach(() => {
metric.step = 7;
});
// Dashboard manipulation
const expectedParams = {
start_time: '2019-08-06T12:40:02.184Z',
end_time: '2019-08-06T20:40:02.184Z',
step: 7,
};
describe('toggleStarredValue', () => {
let unstarredDashboard;
let starredDashboard;
it('uses metric step', done => {
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
beforeEach(() => {
state.isUpdatingStarredValue = false;
[unstarredDashboard, starredDashboard] = dashboardGitResponse;
});
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('performs no changes if no dashboard is selected', () => {
return testAction(toggleStarredValue, null, state, [], []);
});
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
it('performs no changes if already changing starred value', () => {
state.selectedDashboard = unstarredDashboard;
state.isUpdatingStarredValue = true;
return testAction(toggleStarredValue, null, state, [], []);
});
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,
},
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,
},
],
[],
() => {
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');
it('unstars dashboard if it is starred', () => {
state.selectedDashboard = starredDashboard;
mock.onPost(starredDashboard.user_starred_path).reply(200);
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();
});
return testAction(toggleStarredValue, null, state, [
{ type: types.REQUEST_DASHBOARD_STARRING },
{ type: types.RECEIVE_DASHBOARD_STARRING_FAILURE },
]);
});
});
......@@ -1091,29 +1113,26 @@ describe('Monitoring store actions', () => {
});
});
describe('setExpandedPanel', () => {
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 } }],
[],
);
});
});
// Variables manipulation
describe('clearExpandedPanel', () => {
it('Clears a panel as expanded', () => {
return testAction(
clearExpandedPanel,
undefined,
describe('updateVariablesAndFetchData', () => {
it('should commit UPDATE_VARIABLES mutation and fetch data', done => {
testAction(
updateVariablesAndFetchData,
{ pod: 'POD' },
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