diff options
Diffstat (limited to 'spec/javascripts/monitoring/components/dashboard_spec.js')
-rw-r--r-- | spec/javascripts/monitoring/components/dashboard_spec.js | 432 |
1 files changed, 232 insertions, 200 deletions
diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js index 75df2ce3103..0f20171726c 100644 --- a/spec/javascripts/monitoring/components/dashboard_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_spec.js @@ -7,11 +7,12 @@ 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 MonitoringMock, { +import { metricsGroupsAPIResponse, + mockedQueryResultPayload, + mockedQueryResultPayloadCoresTotal, mockApiEndpoint, environmentData, - singleGroupResponse, dashboardGitResponse, } from '../mock_data'; @@ -44,12 +45,33 @@ const resetSpy = spy => { export default propsData; +function setupComponentStore(component) { + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + metricsGroupsAPIResponse, + ); + + // Load 2 panels to the dashboard + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayload, + ); + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayloadCoresTotal, + ); + + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); +} + describe('Dashboard', () => { let DashboardComponent; let mock; let store; let component; - let mockGraphData; beforeEach(() => { setFixtures(` @@ -100,6 +122,32 @@ describe('Dashboard', () => { }); }); + describe('cluster health', () => { + let wrapper; + + beforeEach(done => { + wrapper = shallowMount(DashboardComponent, { + localVue, + sync: false, + propsData: { ...propsData, hasMetrics: true }, + store, + }); + + // all_dashboards is not defined in health dashboards + wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined); + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders correctly', () => { + expect(wrapper.isVueInstance()).toBe(true); + expect(wrapper.exists()).toBe(true); + }); + }); + describe('requests information to the server', () => { let spy; beforeEach(() => { @@ -123,25 +171,6 @@ describe('Dashboard', () => { }); }); - it('hides the legend when showLegend is false', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showLegend: false, - }, - store, - }); - - setTimeout(() => { - expect(component.showEmptyState).toEqual(false); - expect(component.$el.querySelector('.legend-group')).toEqual(null); - expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); - done(); - }); - }); - it('hides the group panels when showPanels is false', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), @@ -153,52 +182,66 @@ describe('Dashboard', () => { store, }); - setTimeout(() => { - expect(component.showEmptyState).toEqual(false); - expect(component.$el.querySelector('.prometheus-panel')).toEqual(null); - expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); - done(); - }); + setupComponentStore(component); + + Vue.nextTick() + .then(() => { + expect(component.showEmptyState).toEqual(false); + expect(component.$el.querySelector('.prometheus-panel')).toEqual(null); + expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); + + done(); + }) + .catch(done.fail); }); - it('renders the environments dropdown with a number of environments', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, + describe('when all the requests have been commited by the store', () => { + beforeEach(() => { + component = new DashboardComponent({ + el: document.querySelector('.prometheus-graphs'), + propsData: { + ...propsData, + hasMetrics: true, + }, + store, + }); + + setupComponentStore(component); }); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); + it('renders the environments dropdown with a number of environments', done => { + Vue.nextTick() + .then(() => { + const dropdownMenuEnvironments = component.$el.querySelectorAll( + '.js-environments-dropdown .dropdown-item', + ); - 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); - 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); + } + }); - Array.from(dropdownMenuEnvironments).forEach((value, index) => { - if (environmentData[index].metrics_path) { - expect(value).toHaveAttr('href', environmentData[index].metrics_path); - } - }); + done(); + }) + .catch(done.fail); + }); - 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 => { @@ -207,15 +250,17 @@ describe('Dashboard', () => { propsData: { ...propsData, hasMetrics: true, - showPanels: false, }, store, }); - component.$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []); component.$store.commit( `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, + metricsGroupsAPIResponse, + ); + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayload, ); Vue.nextTick() @@ -230,7 +275,7 @@ describe('Dashboard', () => { .catch(done.fail); }); - it('renders the environments dropdown with a single active element', done => { + it('renders the datetimepicker dropdown', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), propsData: { @@ -241,64 +286,16 @@ describe('Dashboard', () => { store, }); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); + setupComponentStore(component); Vue.nextTick() .then(() => { - const dropdownItems = component.$el.querySelectorAll( - '.js-environments-dropdown .dropdown-item.active', - ); - - expect(dropdownItems.length).toEqual(1); + expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); done(); }) .catch(done.fail); }); - it('hides the dropdown', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - environmentsEndpoint: '', - }, - store, - }); - - Vue.nextTick(() => { - const dropdownIsActiveElement = component.$el.querySelectorAll('.environments'); - - expect(dropdownIsActiveElement.length).toEqual(0); - done(); - }); - }); - - it('renders the datetimepicker dropdown', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - setTimeout(() => { - expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); - done(); - }); - }); - it('fetches the metrics data with proper time window', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), @@ -347,14 +344,21 @@ describe('Dashboard', () => { el: document.querySelector('.prometheus-graphs'), propsData: { ...propsData, hasMetrics: true }, store, + sync: false, }); - setTimeout(() => { - const selectedTimeWindow = component.$el.querySelector('.js-time-window-dropdown .active'); + setupComponentStore(component); - expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); - done(); - }); + Vue.nextTick() + .then(() => { + const selectedTimeWindow = component.$el.querySelector( + '.js-time-window-dropdown .active', + ); + + expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); + done(); + }) + .catch(done.fail); }); it('shows an error message if invalid url parameters are passed', done => { @@ -381,29 +385,36 @@ describe('Dashboard', () => { describe('drag and drop function', () => { let wrapper; let expectedPanelCount; // also called metrics, naming to be improved: https://gitlab.com/gitlab-org/gitlab/issues/31565 + const findDraggables = () => wrapper.findAll(VueDraggable); const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled')); const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel'); const findRearrangeButton = () => wrapper.find('.js-rearrange-button'); - beforeEach(done => { + beforeEach(() => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - expectedPanelCount = metricsGroupsAPIResponse.data.reduce( - (acc, d) => d.metrics.length + acc, + expectedPanelCount = metricsGroupsAPIResponse.reduce( + (acc, group) => group.panels.length + acc, 0, ); - store.dispatch('monitoringDashboard/setFeatureFlags', { additionalPanelTypesEnabled: true }); + }); + beforeEach(done => { wrapper = shallowMount(DashboardComponent, { localVue, sync: false, propsData: { ...propsData, hasMetrics: true }, store, + attachToDocument: true, }); - // not using $nextTicket becuase we must wait for the dashboard - // to be populated with the mock data results. - setTimeout(done); + setupComponentStore(wrapper.vm); + + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); }); it('wraps vuedraggable', () => { @@ -442,6 +453,28 @@ describe('Dashboard', () => { expect(findEnabledDraggables()).toEqual(findDraggables()); }); + it('metrics can be swapped', done => { + const firstDraggable = findDraggables().at(0); + const mockMetrics = [...metricsGroupsAPIResponse[0].panels]; + const value = () => firstDraggable.props('value'); + + expect(value().length).toBe(mockMetrics.length); + value().forEach((metric, i) => { + expect(metric.title).toBe(mockMetrics[i].title); + }); + + // swap two elements and `input` them + [mockMetrics[0], mockMetrics[1]] = [mockMetrics[1], mockMetrics[0]]; + firstDraggable.vm.$emit('input', mockMetrics); + + firstDraggable.vm.$nextTick(() => { + value().forEach((metric, i) => { + expect(metric.title).toBe(mockMetrics[i].title); + }); + done(); + }); + }); + it('shows a remove button, which removes a panel', done => { expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false); @@ -449,8 +482,6 @@ describe('Dashboard', () => { findFirstDraggableRemoveButton().trigger('click'); wrapper.vm.$nextTick(() => { - // At present graphs will not be removed in backend - // See https://gitlab.com/gitlab-org/gitlab/issues/27835 expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1); done(); }); @@ -466,10 +497,6 @@ describe('Dashboard', () => { }); }); }); - - afterEach(() => { - wrapper.destroy(); - }); }); // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922 @@ -539,42 +566,93 @@ describe('Dashboard', () => { }); }); - describe('when the window resizes', () => { + describe('responds to window resizes', () => { + let promPanel; + let promGroup; + let panelToggle; + let chart; beforeEach(() => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - jasmine.clock().install(); - }); - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it('sets elWidth to page width when the sidebar is resized', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), propsData: { ...propsData, hasMetrics: true, - showPanels: false, + showPanels: true, }, store, }); - expect(component.elWidth).toEqual(0); + setupComponentStore(component); - const pageLayoutEl = document.querySelector('.layout-page'); - pageLayoutEl.classList.add('page-with-icon-sidebar'); + return Vue.nextTick().then(() => { + promPanel = component.$el.querySelector('.prometheus-panel'); + promGroup = promPanel.querySelector('.prometheus-graph-group'); + panelToggle = promPanel.querySelector('.js-graph-group-toggle'); + chart = promGroup.querySelector('.position-relative svg'); + }); + }); - Vue.nextTick() - .then(() => { - jasmine.clock().tick(1000); - return Vue.nextTick(); - }) - .then(() => { - expect(component.elWidth).toEqual(pageLayoutEl.clientWidth); - done(); - }) - .catch(done.fail); + 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); + }); + }); + + describe('dashboard edit link', () => { + let wrapper; + const findEditLink = () => wrapper.find('.js-edit-link'); + + beforeEach(done => { + mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); + + wrapper = shallowMount(DashboardComponent, { + localVue, + sync: false, + attachToDocument: true, + propsData: { ...propsData, hasMetrics: true }, + store, + }); + + wrapper.vm.$store.commit( + `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, + dashboardGitResponse, + ); + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('is not present for the default dashboard', () => { + expect(findEditLink().exists()).toBe(false); + }); + + it('is present for a custom dashboard, and links to its edit_path', done => { + const dashboard = dashboardGitResponse[1]; // non-default dashboard + const currentDashboard = dashboard.path; + + wrapper.setProps({ currentDashboard }); + wrapper.vm.$nextTick(() => { + expect(findEditLink().exists()).toBe(true); + expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path); + done(); + }); }); }); @@ -619,20 +697,6 @@ describe('Dashboard', () => { store, }); - component.$store.dispatch('monitoringDashboard/setFeatureFlags', { - prometheusEndpoint: false, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); - component.$store.commit( `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse, @@ -648,36 +712,4 @@ describe('Dashboard', () => { }); }); }); - - describe('when downloading metrics data as CSV', () => { - beforeEach(() => { - component = new DashboardComponent({ - propsData: { - ...propsData, - }, - store, - }); - store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - MonitoringMock.data, - ); - [mockGraphData] = component.$store.state.monitoringDashboard.groups[0].metrics; - }); - - describe('csvText', () => { - it('converts metrics data from json to csv', () => { - const header = `timestamp,${mockGraphData.y_label}`; - const data = mockGraphData.queries[0].result[0].values; - const firstRow = `${data[0][0]},${data[0][1]}`; - - expect(component.csvText(mockGraphData)).toMatch(`^${header}\r\n${firstRow}`); - }); - }); - - describe('downloadCsv', () => { - it('produces a link with a Blob', () => { - expect(component.downloadCsv(mockGraphData)).toContain(`blob:`); - }); - }); - }); }); |