Commit 3f702298 authored by Jose Vargas's avatar Jose Vargas

Move non EE dashboard specs to jest

This changes the non EE dashboard specs
for the monitoring feature to jest
parent 858555fa
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
mockedQueryResultPayload, mockedQueryResultPayload,
environmentData, environmentData,
} from 'spec/monitoring/mock_data'; } from 'spec/monitoring/mock_data';
import propsData from 'spec/monitoring/components/dashboard_spec'; import propsData from 'spec/monitoring/components/dashboard_resize_spec';
import CustomMetricsFormFields from 'ee/custom_metrics/components/custom_metrics_form_fields.vue'; import CustomMetricsFormFields from 'ee/custom_metrics/components/custom_metrics_form_fields.vue';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
......
import Vue from 'vue'; import { shallowMount, createLocalVue, mount } from '@vue/test-utils';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { GlDropdownItem, GlButton, GlToast } from '@gitlab/ui';
import { GlToast } from '@gitlab/ui';
import VueDraggable from 'vuedraggable'; import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue'; import axios from '~/lib/utils/axios_utils';
import statusCodes from '~/lib/utils/http_status';
import { metricStates } from '~/monitoring/constants'; import { metricStates } from '~/monitoring/constants';
import Dashboard from '~/monitoring/components/dashboard.vue';
import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue';
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue'; import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils'; import * as types from '~/monitoring/stores/mutation_types';
import * as monitoringUtils from '~/monitoring/utils';
import { setupComponentStore, propsData } from '../init_utils';
import { import {
metricsGroupsAPIResponse, metricsGroupsAPIResponse,
mockedEmptyResult,
mockedQueryResultPayload, mockedQueryResultPayload,
mockedQueryResultPayloadCoresTotal,
mockApiEndpoint, mockApiEndpoint,
environmentData, environmentData,
dashboardGitResponse, dashboardGitResponse,
} from '../mock_data'; } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
const propsData = { const expectedPanelCount = 2;
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
metricsEndpoint: mockApiEndpoint,
deploymentsEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
environmentsEndpoint: '/root/hello-prometheus/environments/35',
currentEnvironmentName: 'production',
customMetricsAvailable: false,
customMetricsPath: '',
validateQueryPath: '',
};
const resetSpy = spy => {
if (spy) {
spy.calls.reset();
}
};
let expectedPanelCount;
function setupComponentStore(component) {
// Load 2 panel groups
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
metricsGroupsAPIResponse,
);
// Load 3 panels to the dashboard, one with an empty result
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedEmptyResult,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayload,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayloadCoresTotal,
);
expectedPanelCount = 2;
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
}
describe('Dashboard', () => { describe('Dashboard', () => {
let DashboardComponent; let DashboardComponent;
let mock;
let store; let store;
let component;
let wrapper; let wrapper;
let mock;
const createComponentWrapper = (props = {}, options = {}) => { const createShallowWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(localVue.extend(DashboardComponent), { wrapper = shallowMount(localVue.extend(DashboardComponent), {
localVue, localVue,
sync: false, sync: false,
...@@ -95,21 +39,23 @@ describe('Dashboard', () => { ...@@ -95,21 +39,23 @@ describe('Dashboard', () => {
}); });
}; };
beforeEach(() => { const createMountedWrapper = (props = {}, options = {}) => {
setFixtures(` wrapper = mount(localVue.extend(DashboardComponent), {
<div class="prometheus-graphs"></div> localVue,
<div class="layout-page"></div> sync: false,
`); propsData: { ...propsData, ...props },
store,
...options,
});
};
beforeEach(() => {
store = createStore(); store = createStore();
mock = new MockAdapter(axios);
DashboardComponent = localVue.extend(Dashboard); DashboardComponent = localVue.extend(Dashboard);
mock = new MockAdapter(axios);
}); });
afterEach(() => { afterEach(() => {
if (component) {
component.$destroy();
}
if (wrapper) { if (wrapper) {
wrapper.destroy(); wrapper.destroy();
} }
...@@ -118,37 +64,26 @@ describe('Dashboard', () => { ...@@ -118,37 +64,26 @@ describe('Dashboard', () => {
describe('no metrics are available yet', () => { describe('no metrics are available yet', () => {
beforeEach(() => { beforeEach(() => {
component = new DashboardComponent({ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData }, createShallowWrapper({}, { attachToDocument: true });
store,
});
}); });
it('shows a getting started empty state when no metrics are present', () => { afterEach(() => {
expect(component.$el.querySelector('.prometheus-graphs')).toBe(null); wrapper.destroy();
expect(component.emptyState).toEqual('gettingStarted');
}); });
it('shows the environment selector', () => { it('shows the environment selector', () => {
expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy(); expect(wrapper.vm.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
}); });
}); });
describe('no data found', () => { describe('no data found', () => {
it('shows the environment selector dropdown', () => {
createComponentWrapper();
expect(wrapper.find('.js-environments-dropdown').exists()).toBeTruthy();
});
});
describe('cluster health', () => {
beforeEach(done => { beforeEach(done => {
createComponentWrapper({ hasMetrics: true }); mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
createShallowWrapper({}, { attachToDocument: true });
// all_dashboards is not defined in health dashboards
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
wrapper.vm.$nextTick(done); wrapper.vm.$nextTick(done);
}); });
...@@ -156,254 +91,186 @@ describe('Dashboard', () => { ...@@ -156,254 +91,186 @@ describe('Dashboard', () => {
wrapper.destroy(); wrapper.destroy();
}); });
it('renders correctly', () => { it('shows the environment selector dropdown', () => {
expect(wrapper.isVueInstance()).toBe(true); expect(wrapper.vm.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
expect(wrapper.exists()).toBe(true);
}); });
}); });
describe('requests information to the server', () => { describe('request information to the server', () => {
let spy;
beforeEach(() => { beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
}); });
afterEach(() => {
resetSpy(spy);
});
it('shows up a loading state', done => { it('shows up a loading state', done => {
component = new DashboardComponent({ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true },
store,
});
Vue.nextTick(() => {
expect(component.emptyState).toEqual('loading');
done();
});
});
it('hides the group panels when showPanels is false', done => { wrapper.vm
component = new DashboardComponent({ .$nextTick()
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
},
store,
});
setupComponentStore(component);
Vue.nextTick()
.then(() => { .then(() => {
expect(component.showEmptyState).toEqual(false); expect(wrapper.vm.emptyState).toEqual('loading');
expect(component.$el.querySelector('.prometheus-panel')).toEqual(null);
expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
describe('when all the requests have been commited by the store', () => { it('hides the group panels when showPanels is false', done => {
beforeEach(() => { createMountedWrapper(
component = new DashboardComponent({ { hasMetrics: true, showPanels: false },
el: document.querySelector('.prometheus-graphs'), { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
propsData: {
...propsData,
hasMetrics: true,
},
store,
});
setupComponentStore(component);
});
it('renders the environments dropdown with a number of environments', done => {
Vue.nextTick()
.then(() => {
const dropdownMenuEnvironments = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item',
);
expect(component.environments.length).toEqual(environmentData.length);
expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
Array.from(dropdownMenuEnvironments).forEach((value, index) => {
if (environmentData[index].metrics_path) {
expect(value).toHaveAttr('href', environmentData[index].metrics_path);
}
});
done();
})
.catch(done.fail);
});
it('renders the environments dropdown with a single active element', done => {
Vue.nextTick()
.then(() => {
const dropdownItems = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item.active',
);
expect(dropdownItems.length).toEqual(1);
done();
})
.catch(done.fail);
});
});
it('hides the environments dropdown list when there is no environments', done => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
},
store,
});
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
metricsGroupsAPIResponse,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayload,
); );
Vue.nextTick() setupComponentStore(wrapper);
wrapper.vm
.$nextTick()
.then(() => { .then(() => {
const dropdownMenuEnvironments = component.$el.querySelectorAll( expect(wrapper.vm.showEmptyState).toEqual(false);
'.js-environments-dropdown .dropdown-item', expect(wrapper.vm.$el.querySelector('.prometheus-panel')).toEqual(null);
); // TODO: The last expectation doesn't belong here, it belongs in a `group_group_spec.js` file
// Issue: https://gitlab.com/gitlab-org/gitlab/issues/118780
// expect(wrapper.vm.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
expect(dropdownMenuEnvironments.length).toEqual(0);
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
it('renders the datetimepicker dropdown', done => { it('fetches the metrics data with proper time window', done => {
component = new DashboardComponent({ const getTimeDiffSpy = jest.spyOn(monitoringUtils, 'getTimeDiff');
el: document.querySelector('.prometheus-graphs'), jest.spyOn(store, 'dispatch');
propsData: {
...propsData, createMountedWrapper(
hasMetrics: true, { hasMetrics: true },
showPanels: false, { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
}, );
store,
});
setupComponentStore(component); wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
Vue.nextTick() wrapper.vm
.$nextTick()
.then(() => { .then(() => {
expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); expect(store.dispatch).toHaveBeenCalled();
expect(getTimeDiffSpy).toHaveBeenCalled();
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
});
it('fetches the metrics data with proper time window', done => { describe('when all requests have been commited by the store', () => {
component = new DashboardComponent({ beforeEach(() => {
el: document.querySelector('.prometheus-graphs'), mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
},
store,
});
spyOn(component.$store, 'dispatch').and.stub();
const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough();
component.$store.commit( createMountedWrapper(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, { hasMetrics: true },
environmentData, { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
); );
component.$mount(); setupComponentStore(wrapper);
});
afterEach(() => {
wrapper.destroy();
});
Vue.nextTick() it('renders the environments dropdown with a number of environments', done => {
wrapper.vm
.$nextTick()
.then(() => { .then(() => {
expect(component.$store.dispatch).toHaveBeenCalled(); const environmentDropdownItems = wrapper
expect(getTimeDiffSpy).toHaveBeenCalled(); .find('.js-environments-dropdown')
.findAll(GlDropdownItem);
expect(wrapper.vm.environments.length).toEqual(environmentData.length);
expect(environmentDropdownItems.length).toEqual(wrapper.vm.environments.length);
environmentDropdownItems.wrappers.forEach((itemWrapper, index) => {
const anchorEl = itemWrapper.find('a');
if (anchorEl.exists() && environmentData[index].metrics_path) {
const href = anchorEl.attributes('href');
expect(href).toBe(environmentData[index].metrics_path);
}
});
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
it('shows a specific time window selected from the url params', done => { it('renders the environments dropdown with a single active element', done => {
const start = '2019-10-01T18:27:47.000Z'; wrapper.vm
const end = '2019-10-01T18:57:47.000Z'; .$nextTick()
spyOnDependency(Dashboard, 'getTimeDiff').and.returnValue({
start,
end,
});
spyOnDependency(Dashboard, 'getParameterValues').and.callFake(param => {
if (param === 'start') return [start];
if (param === 'end') return [end];
return [];
});
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true },
store,
sync: false,
});
setupComponentStore(component);
Vue.nextTick()
.then(() => { .then(() => {
const selectedTimeWindow = component.$el.querySelector( const environmentDropdownItems = wrapper
'.js-time-window-dropdown .active', .find('.js-environments-dropdown')
.findAll(GlDropdownItem);
const activeItem = environmentDropdownItems.wrappers.filter(itemWrapper =>
itemWrapper.find('.active').exists(),
); );
expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); expect(activeItem.length).toBe(1);
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
});
it('shows an error message if invalid url parameters are passed', done => { it('hides the environments dropdown list when there is no environments', done => {
spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([ createMountedWrapper(
'<script>alert("XSS")</script>', { hasMetrics: true },
]); { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
metricsGroupsAPIResponse,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayload,
);
wrapper.vm
.$nextTick()
.then(() => {
const environmentDropdownItems = wrapper
.find('.js-environments-dropdown')
.findAll(GlDropdownItem);
expect(environmentDropdownItems.length).toEqual(0);
done();
})
.catch(done.fail);
});
component = new DashboardComponent({ it('renders the datetimepicker dropdown', done => {
el: document.querySelector('.prometheus-graphs'), createMountedWrapper(
propsData: { ...propsData, hasMetrics: true }, { hasMetrics: true },
store, { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
}); );
spy = spyOn(component, 'showInvalidDateError'); setupComponentStore(wrapper);
component.$mount();
component.$nextTick(() => { wrapper.vm
expect(component.showInvalidDateError).toHaveBeenCalled(); .$nextTick()
.then(() => {
expect(wrapper.find(DateTimePicker).exists()).toBe(true);
done(); done();
}); })
}); .catch(done.fail);
}); });
describe('when one of the metrics is missing', () => { describe('when one of the metrics is missing', () => {
beforeEach(() => { beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
});
beforeEach(done => { createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
createComponentWrapper({ hasMetrics: true }); setupComponentStore(wrapper);
setupComponentStore(wrapper.vm);
wrapper.vm.$nextTick(done); wrapper.vm.$nextTick(done);
}); });
...@@ -432,13 +299,13 @@ describe('Dashboard', () => { ...@@ -432,13 +299,13 @@ describe('Dashboard', () => {
const findRearrangeButton = () => wrapper.find('.js-rearrange-button'); const findRearrangeButton = () => wrapper.find('.js-rearrange-button');
beforeEach(() => { beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
}); });
beforeEach(done => { beforeEach(done => {
createComponentWrapper({ hasMetrics: true }, { attachToDocument: true }); createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
setupComponentStore(wrapper.vm); setupComponentStore(wrapper);
wrapper.vm.$nextTick(done); wrapper.vm.$nextTick(done);
}); });
...@@ -527,110 +394,23 @@ describe('Dashboard', () => { ...@@ -527,110 +394,23 @@ describe('Dashboard', () => {
}); });
}); });
// https://gitlab.com/gitlab-org/gitlab-ce/issues/66922 describe('cluster health', () => {
// eslint-disable-next-line jasmine/no-disabled-tests
xdescribe('link to chart', () => {
const currentDashboard = 'TEST_DASHBOARD';
localVue.use(GlToast);
const link = () => wrapper.find('.js-chart-link');
const clipboardText = () => link().element.dataset.clipboardText;
beforeEach(done => { beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(propsData.metricsEndpoint).reply(statusCodes.OK, JSON.stringify({}));
createShallowWrapper({ hasMetrics: true });
createComponentWrapper({ hasMetrics: true, currentDashboard }, { attachToDocument: true });
setTimeout(done); // all_dashboards is not defined in health dashboards
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
wrapper.vm.$nextTick(done);
}); });
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
it('adds a copy button to the dropdown', () => { it('renders correctly', () => {
expect(link().text()).toContain('Generate link to chart'); expect(wrapper.isVueInstance()).toBe(true);
}); expect(wrapper.exists()).toBe(true);
it('contains a link to the dashboard', () => {
expect(clipboardText()).toContain(`dashboard=${currentDashboard}`);
expect(clipboardText()).toContain(`group=`);
expect(clipboardText()).toContain(`title=`);
expect(clipboardText()).toContain(`y_label=`);
});
it('undefined parameter is stripped', done => {
wrapper.setProps({ currentDashboard: undefined });
wrapper.vm.$nextTick(() => {
expect(clipboardText()).not.toContain(`dashboard=`);
expect(clipboardText()).toContain(`y_label=`);
done();
});
});
it('null parameter is stripped', done => {
wrapper.setProps({ currentDashboard: null });
wrapper.vm.$nextTick(() => {
expect(clipboardText()).not.toContain(`dashboard=`);
expect(clipboardText()).toContain(`y_label=`);
done();
});
});
it('creates a toast when clicked', () => {
spyOn(wrapper.vm.$toast, 'show').and.stub();
link().vm.$emit('click');
expect(wrapper.vm.$toast.show).toHaveBeenCalled();
});
});
describe('responds to window resizes', () => {
let promPanel;
let promGroup;
let panelToggle;
let chart;
beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: true,
},
store,
});
setupComponentStore(component);
return Vue.nextTick().then(() => {
[, promPanel] = component.$el.querySelectorAll('.prometheus-panel');
promGroup = promPanel.querySelector('.prometheus-graph-group');
panelToggle = promPanel.querySelector('.js-graph-group-toggle');
chart = promGroup.querySelector('.position-relative svg');
});
});
it('setting chart size to zero when panel group is hidden', () => {
expect(promGroup.style.display).toBe('');
expect(chart.clientWidth).toBeGreaterThan(0);
panelToggle.click();
return Vue.nextTick().then(() => {
expect(promGroup.style.display).toBe('none');
expect(chart.clientWidth).toBe(0);
promPanel.style.width = '500px';
});
});
it('expanding chart panel group after resize displays chart', () => {
panelToggle.click();
expect(chart.clientWidth).toBeGreaterThan(0);
}); });
}); });
...@@ -638,9 +418,9 @@ describe('Dashboard', () => { ...@@ -638,9 +418,9 @@ describe('Dashboard', () => {
const findEditLink = () => wrapper.find('.js-edit-link'); const findEditLink = () => wrapper.find('.js-edit-link');
beforeEach(done => { beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
createComponentWrapper({ hasMetrics: true }, { attachToDocument: true }); createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
wrapper.vm.$store.commit( wrapper.vm.$store.commit(
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
...@@ -662,11 +442,42 @@ describe('Dashboard', () => { ...@@ -662,11 +442,42 @@ describe('Dashboard', () => {
const currentDashboard = dashboard.path; const currentDashboard = dashboard.path;
wrapper.setProps({ currentDashboard }); wrapper.setProps({ currentDashboard });
wrapper.vm.$nextTick(() => { wrapper.vm
expect(findEditLink().exists()).toBe(true); .$nextTick()
expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path); .then(() => {
done(); expect(findEditLink().exists()).toBe(true);
}); expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
done();
})
.catch(done.fail);
});
});
describe('Dashboard dropdown', () => {
beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
createMountedWrapper(
{ hasMetrics: true },
{ attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
dashboardGitResponse,
);
});
it('shows the dashboard dropdown', done => {
wrapper.vm
.$nextTick()
.then(() => {
const dashboardDropdown = wrapper.find('.js-dashboards-dropdown');
expect(dashboardDropdown.exists()).toBe(true);
done();
})
.catch(done.fail);
}); });
}); });
...@@ -674,56 +485,89 @@ describe('Dashboard', () => { ...@@ -674,56 +485,89 @@ describe('Dashboard', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
component = new DashboardComponent({ createMountedWrapper(
el: document.querySelector('.prometheus-graphs'), {
propsData: {
...propsData,
hasMetrics: true, hasMetrics: true,
showPanels: false, showPanels: false,
showTimeWindowDropdown: false, showTimeWindowDropdown: false,
externalDashboardUrl: '/mockUrl', externalDashboardUrl: '/mockUrl',
}, },
store, { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
}); );
}); });
it('shows the link', done => { it('shows the link', done => {
setTimeout(() => { wrapper.vm
expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain( .$nextTick()
'View full dashboard', .then(() => {
); const externalDashboardButton = wrapper.find('.js-external-dashboard-link');
done();
}); expect(externalDashboardButton.exists()).toBe(true);
expect(externalDashboardButton.is(GlButton)).toBe(true);
expect(externalDashboardButton.text()).toContain('View full dashboard');
done();
})
.catch(done.fail);
}); });
}); });
describe('Dashboard dropdown', () => { // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
beforeEach(() => { // eslint-disable-next-line jest/no-disabled-tests
describe.skip('link to chart', () => {
const currentDashboard = 'TEST_DASHBOARD';
localVue.use(GlToast);
const link = () => wrapper.find('.js-chart-link');
const clipboardText = () => link().element.dataset.clipboardText;
beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
component = new DashboardComponent({ createShallowWrapper({ hasMetrics: true, currentDashboard }, { attachToDocument: true });
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
},
store,
});
component.$store.commit( setTimeout(done);
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
dashboardGitResponse,
);
}); });
it('shows the dashboard dropdown', done => { afterEach(() => {
setTimeout(() => { wrapper.destroy();
const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown'); });
it('adds a copy button to the dropdown', () => {
expect(link().text()).toContain('Generate link to chart');
});
it('contains a link to the dashboard', () => {
expect(clipboardText()).toContain(`dashboard=${currentDashboard}`);
expect(clipboardText()).toContain(`group=`);
expect(clipboardText()).toContain(`title=`);
expect(clipboardText()).toContain(`y_label=`);
});
expect(dashboardDropdown).not.toEqual(null); it('undefined parameter is stripped', done => {
wrapper.setProps({ currentDashboard: undefined });
wrapper.vm.$nextTick(() => {
expect(clipboardText()).not.toContain(`dashboard=`);
expect(clipboardText()).toContain(`y_label=`);
done(); done();
}); });
}); });
it('null parameter is stripped', done => {
wrapper.setProps({ currentDashboard: null });
wrapper.vm.$nextTick(() => {
expect(clipboardText()).not.toContain(`dashboard=`);
expect(clipboardText()).toContain(`y_label=`);
done();
});
});
it('creates a toast when clicked', () => {
jest.spyOn(wrapper.vm.$toast, 'show').and.stub();
link().vm.$emit('click');
expect(wrapper.vm.$toast.show).toHaveBeenCalled();
});
}); });
}); });
import { mount, createLocalVue } from '@vue/test-utils';
import createFlash from '~/flash';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { createStore } from '~/monitoring/stores';
import { propsData } from '../init_utils';
const localVue = createLocalVue();
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
getParameterValues: jest.fn().mockReturnValue('<script>alert("XSS")</script>'),
}));
describe('dashboard invalid url parameters', () => {
let store;
let wrapper;
const createMountedWrapper = (props = {}, options = {}) => {
wrapper = mount(localVue.extend(Dashboard), {
localVue,
sync: false,
propsData: { ...propsData, ...props },
store,
...options,
});
};
beforeEach(() => {
store = createStore();
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
it('shows an error message if invalid url parameters are passed', done => {
createMountedWrapper(
{ hasMetrics: true },
{ attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
);
wrapper.vm
.$nextTick()
.then(() => {
expect(createFlash).toHaveBeenCalled();
done();
})
.catch(done.fail);
});
});
import { mount, createLocalVue } from '@vue/test-utils';
import { GlDropdownItem } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import statusCodes from '~/lib/utils/http_status';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { createStore } from '~/monitoring/stores';
import { propsData, setupComponentStore } from '../init_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint } from '../mock_data';
const localVue = createLocalVue();
jest.mock('~/lib/utils/url_utility', () => ({
getParameterValues: jest.fn().mockImplementation(param => {
if (param === 'start') return ['2019-10-01T18:27:47.000Z'];
if (param === 'end') return ['2019-10-01T18:57:47.000Z'];
return [];
}),
mergeUrlParams: jest.fn().mockReturnValue('#'),
}));
describe('dashboard time window', () => {
let store;
let wrapper;
let mock;
const createComponentWrapperMounted = (props = {}, options = {}) => {
wrapper = mount(localVue.extend(Dashboard), {
localVue,
sync: false,
propsData: { ...propsData, ...props },
store,
...options,
});
};
beforeEach(() => {
store = createStore();
mock = new MockAdapter(axios);
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
mock.restore();
});
it('shows an error message if invalid url parameters are passed', done => {
mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
createComponentWrapperMounted(
{ hasMetrics: true },
{ attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
);
setupComponentStore(wrapper);
wrapper.vm
.$nextTick()
.then(() => {
const timeWindowDropdownItems = wrapper
.find('.js-time-window-dropdown')
.findAll(GlDropdownItem);
const activeItem = timeWindowDropdownItems.wrappers.filter(itemWrapper =>
itemWrapper.find('.active').exists(),
);
expect(activeItem.length).toBe(1);
done();
})
.catch(done.fail);
});
});
import * as types from '~/monitoring/stores/mutation_types';
import {
metricsGroupsAPIResponse,
mockedEmptyResult,
mockedQueryResultPayload,
mockedQueryResultPayloadCoresTotal,
mockApiEndpoint,
environmentData,
} from './mock_data';
export const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
metricsEndpoint: mockApiEndpoint,
deploymentsEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
environmentsEndpoint: '/root/hello-prometheus/environments/35',
currentEnvironmentName: 'production',
customMetricsAvailable: false,
customMetricsPath: '',
validateQueryPath: '',
};
export const setupComponentStore = wrapper => {
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
metricsGroupsAPIResponse,
);
// Load 3 panels to the dashboard, one with an empty result
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedEmptyResult,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayload,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayloadCoresTotal,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
};
import Vue from 'vue';
import { createLocalVue } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils';
import {
metricsGroupsAPIResponse,
mockedEmptyResult,
mockedQueryResultPayload,
mockedQueryResultPayloadCoresTotal,
mockApiEndpoint,
environmentData,
} from '../mock_data';
const localVue = createLocalVue();
const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
metricsEndpoint: mockApiEndpoint,
deploymentsEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
environmentsEndpoint: '/root/hello-prometheus/environments/35',
currentEnvironmentName: 'production',
customMetricsAvailable: false,
customMetricsPath: '',
validateQueryPath: '',
};
function setupComponentStore(component) {
// Load 2 panel groups
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
metricsGroupsAPIResponse,
);
// Load 3 panels to the dashboard, one with an empty result
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedEmptyResult,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayload,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayloadCoresTotal,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
}
describe('Dashboard', () => {
let DashboardComponent;
let mock;
let store;
let component;
let wrapper;
beforeEach(() => {
setFixtures(`
<div class="prometheus-graphs"></div>
<div class="layout-page"></div>
`);
store = createStore();
mock = new MockAdapter(axios);
DashboardComponent = localVue.extend(Dashboard);
});
afterEach(() => {
if (component) {
component.$destroy();
}
if (wrapper) {
wrapper.destroy();
}
mock.restore();
});
describe('responds to window resizes', () => {
let promPanel;
let promGroup;
let panelToggle;
let chart;
beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: true,
},
store,
});
setupComponentStore(component);
return Vue.nextTick().then(() => {
[, promPanel] = component.$el.querySelectorAll('.prometheus-panel');
promGroup = promPanel.querySelector('.prometheus-graph-group');
panelToggle = promPanel.querySelector('.js-graph-group-toggle');
chart = promGroup.querySelector('.position-relative svg');
});
});
it('setting chart size to zero when panel group is hidden', () => {
expect(promGroup.style.display).toBe('');
expect(chart.clientWidth).toBeGreaterThan(0);
panelToggle.click();
return Vue.nextTick().then(() => {
expect(promGroup.style.display).toBe('none');
expect(chart.clientWidth).toBe(0);
promPanel.style.width = '500px';
});
});
it('expanding chart panel group after resize displays chart', () => {
panelToggle.click();
expect(chart.clientWidth).toBeGreaterThan(0);
});
});
});
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