From d8f24afa6a98a4b55ac4af7c0eabef63fe9fb48d Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Fri, 6 Sep 2019 08:18:40 +0000 Subject: Move monitoring dashboard_spec to Jest --- spec/frontend/environment.js | 1 + .../monitoring/components/dashboard_spec.js | 582 ++++++++++++++++++++ spec/frontend/test_setup.js | 3 + .../monitoring/components/dashboard_spec.js | 590 --------------------- 4 files changed, 586 insertions(+), 590 deletions(-) create mode 100644 spec/frontend/monitoring/components/dashboard_spec.js delete mode 100644 spec/javascripts/monitoring/components/dashboard_spec.js diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js index 290c0e797cb..a8e42721bf0 100644 --- a/spec/frontend/environment.js +++ b/spec/frontend/environment.js @@ -40,6 +40,7 @@ class CustomEnvironment extends JSDOMEnvironment { this.global.fixturesBasePath = `${ROOT_PATH}/tmp/tests/frontend/fixtures${IS_EE ? '-ee' : ''}`; this.global.staticFixturesBasePath = `${ROOT_PATH}/spec/frontend/fixtures`; + this.global.IS_EE = IS_EE; // Not yet supported by JSDOM: https://github.com/jsdom/jsdom/issues/317 this.global.document.createRange = () => ({ diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js new file mode 100644 index 00000000000..2a8e0240bf9 --- /dev/null +++ b/spec/frontend/monitoring/components/dashboard_spec.js @@ -0,0 +1,582 @@ +import Vue from 'vue'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlToast, GlDropdownItem } from '@gitlab/ui'; +import MockAdapter from 'axios-mock-adapter'; +import Dashboard from '~/monitoring/components/dashboard.vue'; +import GraphGroup from '~/monitoring/components/graph_group.vue'; +import EmptyState from '~/monitoring/components/empty_state.vue'; +import { timeWindows } from '~/monitoring/constants'; +import * as types from '~/monitoring/stores/mutation_types'; +import { createStore } from '~/monitoring/stores'; +import axios from '~/lib/utils/axios_utils'; + +// TODO: replace with dynamic fixture +// https://gitlab.com/gitlab-org/gitlab-ce/issues/62785 +import MonitoringMock, { + metricsGroupsAPIResponse, + mockApiEndpoint, + environmentData, + singleGroupResponse, + dashboardGitResponse, +} from '../../../../spec/javascripts/monitoring/mock_data'; + +/* eslint-disable no-unused-vars */ +/* eslint-disable no-undef */ +// see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/32571#note_211860465 +function setupComponentStore(component) { + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + metricsGroupsAPIResponse, + ); + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayload, + ); + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); +} + +// Mock imported files while retaining the original behaviour +// See https://github.com/facebook/jest/issues/936#issuecomment-265074320 +function mockMonitoringUtils() { + const original = require.requireActual('~/monitoring/utils'); + return { + ...original, // Pass down all the exported objects + getTimeDiff: jest.spyOn(original, 'getTimeDiff'), + }; +} +jest.mock('~/monitoring/utils', () => mockMonitoringUtils()); +const monitoringUtils = require.requireMock('~/monitoring/utils'); + +function mockUrlUtility() { + const original = require.requireActual('~/lib/utils/url_utility'); + return { + ...original, // Pass down all the exported objects + getParameterValues: jest.spyOn(original, 'getParameterValues'), + }; +} +jest.mock('~/lib/utils/url_utility', () => mockUrlUtility()); +const urlUtility = require.requireMock('~/lib/utils/url_utility'); + +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', + emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg', + environmentsEndpoint: '/root/hello-prometheus/environments/35', + currentEnvironmentName: 'production', + customMetricsAvailable: false, + customMetricsPath: '', + validateQueryPath: '', +}; + +export default propsData; + +describe('Dashboard', () => { + let DashboardComponent; + let mock; + let store; + let component; + let mockGraphData; + + beforeEach(() => { + setFixtures(` +
+
+ `); + + window.gon = { + ...window.gon, + ee: false, + }; + + store = createStore(); + mock = new MockAdapter(axios); + DashboardComponent = Vue.extend(Dashboard); + }); + + afterEach(() => { + if (component) { + component.$destroy(); + } + mock.restore(); + }); + + describe('no metrics are available yet', () => { + beforeEach(() => { + component = new DashboardComponent({ + el: document.querySelector('.prometheus-graphs'), + propsData: { ...propsData }, + store, + }); + }); + + it('shows a getting started empty state when no metrics are present', () => { + expect(component.$el.querySelector('.prometheus-graphs')).toBe(null); + expect(component.emptyState).toEqual('gettingStarted'); + }); + + it('shows the environment selector', () => { + expect(component.$el.querySelector('#monitor-environments-dropdown')).toBeTruthy(); + }); + }); + + describe('no data found', () => { + it('shows the environment selector dropdown', () => { + component = new DashboardComponent({ + el: document.querySelector('.prometheus-graphs'), + propsData: { ...propsData, showEmptyState: true }, + store, + }); + + expect(component.$el.querySelector('#monitor-environments-dropdown')).toBeTruthy(); + }); + }); + + describe('requests information to the server', () => { + beforeEach(() => { + mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); + }); + + it('shows up a loading state', done => { + component = new DashboardComponent({ + 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 => { + const wrapper = shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + }, + store, + sync: false, + localVue, + }); + setImmediate(() => { + expect(wrapper.find(EmptyState).exists()).toBe(false); + expect(wrapper.find(GraphGroup).exists()).toBe(true); + expect(wrapper.find(GraphGroup).props().showPanels).toBe(false); + done(); + }); + }); + + it('renders the environments dropdown with a number of environments', () => { + const wrapper = shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + }, + store, + }); + + store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); + store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + singleGroupResponse, + ); + + Vue.nextTick(() => { + const dropdownMenuEnvironments = wrapper + .find('.js-environments-dropdown') + .findAll(GlDropdownItem); + + expect(environmentData.length).toBeGreaterThan(0); + expect(dropdownMenuEnvironments.length).toEqual(environmentData.length); + dropdownMenuEnvironments.wrappers.forEach((value, index) => { + expect(value.attributes('href')).toEqual(environmentData[index].metrics_path); + }); + }); + }); + + it('hides the environments dropdown list when there is no environments', () => { + const wrapper = shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + }, + store, + }); + + const findEnvironmentsDropdownItems = () => wrapper.find('#monitor-environments-wrapper'); + + store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []); + store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + singleGroupResponse, + ); + + return Vue.nextTick(() => { + expect(findEnvironmentsDropdownItems(wrapper).exists()).toEqual(false); + }); + }); + + it('renders the environments dropdown with a single active element', () => { + const wrapper = shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + }, + store, + sync: false, + localVue, + }); + + store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); + store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + singleGroupResponse, + ); + + Vue.nextTick(() => { + const activeDropdownMenuEnvironments = wrapper + .find('#monitor-environments-dropdown') + .findAll(GlDropdownItem) + .filter(item => item.attributes('active') === 'true'); + + expect(activeDropdownMenuEnvironments.length).toEqual(1); + }); + }); + + 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 time window dropdown with a set of options', done => { + const wrapper = shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + sync: false, + }, + store, + }); + + const numberOfTimeWindows = Object.keys(timeWindows).length; + + setImmediate(() => { + const timeWindowDropdown = wrapper.find('.js-time-window-dropdown'); + const timeWindowDropdownEls = wrapper + .find('.js-time-window-dropdown') + .findAll(GlDropdownItem); + + expect(timeWindowDropdown.exists()).toBe(true); + expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows); + + done(); + }); + }); + + it('fetches the metrics data with proper time window', () => { + jest.spyOn(store, 'dispatch').mockImplementationOnce(() => {}); + + store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); + + shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + }, + store, + }); + + const defaultRange = monitoringUtils.getTimeDiff(); + return Vue.nextTick().then(() => { + expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', defaultRange); + }); + }); + + it('shows a specific time window selected from the url params', done => { + const start = 1564439536; + const end = 1564441336; + monitoringUtils.getTimeDiff.mockReturnValueOnce({ + start, + end, + }); + urlUtility.getParameterValues.mockImplementationOnce(param => { + if (param === 'start') return [start]; + if (param === 'end') return [end]; + return []; + }); + + const wrapper = shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + }, + store, + }); + + setImmediate(() => { + const activeTimeWindowItems = wrapper + .find('.js-time-window-dropdown') + .findAll(GlDropdownItem) + .filter(item => item.attributes('active') === 'true'); + + expect(activeTimeWindowItems.length).toEqual(1); + expect(activeTimeWindowItems.wrappers[0].text().trim()).toEqual('30 minutes'); + + done(); + }); + }); + }); + + describe('link to chart', () => { + let wrapper; + 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); + + wrapper = shallowMount(DashboardComponent, { + localVue, + propsData: { ...propsData, hasMetrics: true, currentDashboard }, + store, + }); + + setImmediate(() => { + done(); + }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + 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=`); + }); + + 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', () => { + jest.spyOn(wrapper.vm.$toast, 'show').mockImplementation(() => {}); + + link().vm.$emit('click'); + + expect(wrapper.vm.$toast.show).toHaveBeenCalled(); + }); + }); + + describe('when the window resizes', () => { + beforeEach(() => { + mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); + }); + + 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, + }, + store, + }); + + expect(component.elWidth).toEqual(0); + + const pageLayoutEl = document.querySelector('.layout-page'); + pageLayoutEl.classList.add('page-with-icon-sidebar'); + + Vue.nextTick() + .then(() => { + jest.advanceTimersByTime(1000); + return Vue.nextTick(); + }) + .then(() => { + expect(component.elWidth).toEqual(pageLayoutEl.clientWidth); + done(); + }) + .catch(done.fail); + }); + }); + + describe('external dashboard link', () => { + let wrapper; + + beforeEach(done => { + mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); + + wrapper = shallowMount(DashboardComponent, { + propsData: { + ...propsData, + hasMetrics: true, + showPanels: false, + showTimeWindowDropdown: false, + externalDashboardUrl: '/mockUrl', + }, + store, + sync: false, + localVue, + }); + + setImmediate(done); + }); + + it('shows the link', () => { + expect(wrapper.find('.js-external-dashboard-link').text()).toContain('View full dashboard'); + }); + }); + + describe('Dashboard dropdown', () => { + let wrapper; + + beforeEach(done => { + mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); + + wrapper = shallowMount(DashboardComponent, { + propsData: { ...propsData, hasMetrics: true, showPanels: false }, + store, + sync: false, + localVue, + }); + + setImmediate(() => { + store.dispatch('monitoringDashboard/setFeatureFlags', { + prometheusEndpoint: false, + multipleDashboardsEnabled: true, + }); + + store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); + store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + singleGroupResponse, + ); + + store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse); + done(); + }); + }); + + it('shows the dashboard dropdown', () => { + expect(wrapper.find('.js-dashboards-dropdown').exists()).toEqual(true); + }); + }); + + 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)).toContain(`${header}\r\n${firstRow}`); + }); + }); + + describe('downloadCsv', () => { + let spy; + + beforeEach(() => { + spy = jest.spyOn(window.URL, 'createObjectURL'); + }); + + afterEach(() => { + spy.mockRestore(); + }); + + it('creates a string containing a URL that represents the object', () => { + component.downloadCsv(mockGraphData); + + expect(spy).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js index d52aeb1fe6b..e7944441fe1 100644 --- a/spec/frontend/test_setup.js +++ b/spec/frontend/test_setup.js @@ -73,6 +73,9 @@ expect.extend(customMatchers); // Tech debt issue TBD testUtilsConfig.logModifiedComponents = false; +// Stub for URL.createObjectURL +window.URL.createObjectURL = function createObjectURL() {}; + // Basic stub for MutationObserver global.MutationObserver = () => ({ disconnect: () => {}, diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js deleted file mode 100644 index 15e41e2fe93..00000000000 --- a/spec/javascripts/monitoring/components/dashboard_spec.js +++ /dev/null @@ -1,590 +0,0 @@ -import Vue from 'vue'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { GlToast } from '@gitlab/ui'; -import MockAdapter from 'axios-mock-adapter'; -import Dashboard from '~/monitoring/components/dashboard.vue'; -import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants'; -import * as types from '~/monitoring/stores/mutation_types'; -import { createStore } from '~/monitoring/stores'; -import axios from '~/lib/utils/axios_utils'; -import MonitoringMock, { - metricsGroupsAPIResponse, - mockApiEndpoint, - environmentData, - singleGroupResponse, - dashboardGitResponse, -} 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', - emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg', - environmentsEndpoint: '/root/hello-prometheus/environments/35', - currentEnvironmentName: 'production', - customMetricsAvailable: false, - customMetricsPath: '', - validateQueryPath: '', -}; - -export default propsData; - -describe('Dashboard', () => { - let DashboardComponent; - let mock; - let store; - let component; - let mockGraphData; - - beforeEach(() => { - setFixtures(` -
-
- `); - - window.gon = { - ...window.gon, - ee: false, - }; - - store = createStore(); - mock = new MockAdapter(axios); - DashboardComponent = Vue.extend(Dashboard); - }); - - afterEach(() => { - if (component) { - component.$destroy(); - } - mock.restore(); - }); - - describe('no metrics are available yet', () => { - beforeEach(() => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { ...propsData }, - store, - }); - }); - - it('shows a getting started empty state when no metrics are present', () => { - expect(component.$el.querySelector('.prometheus-graphs')).toBe(null); - expect(component.emptyState).toEqual('gettingStarted'); - }); - - it('shows the environment selector', () => { - expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy(); - }); - }); - - describe('no data found', () => { - it('shows the environment selector dropdown', () => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { ...propsData, showEmptyState: true }, - store, - }); - - expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy(); - }); - }); - - describe('requests information to the server', () => { - beforeEach(() => { - mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - }); - - it('shows up a loading state', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { ...propsData, hasMetrics: true }, - store, - }); - - Vue.nextTick(() => { - expect(component.emptyState).toEqual('loading'); - done(); - }); - }); - - 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'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - setTimeout(() => { - expect(component.showEmptyState).toEqual(false); - expect(component.$el.querySelector('.prometheus-panel')).toEqual(null); - expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); - done(); - }); - }); - - 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, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); - - 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('hides the environments dropdown list when there is no environments', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - 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, - ); - - Vue.nextTick() - .then(() => { - const dropdownMenuEnvironments = component.$el.querySelectorAll( - '.js-environments-dropdown .dropdown-item', - ); - - expect(dropdownMenuEnvironments.length).toEqual(0); - done(); - }) - .catch(done.fail); - }); - - it('renders the environments dropdown with a single active element', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); - - 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 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 time window dropdown with a set of options', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - const numberOfTimeWindows = Object.keys(timeWindows).length; - - setTimeout(() => { - const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown'); - const timeWindowDropdownEls = component.$el.querySelectorAll( - '.js-time-window-dropdown .dropdown-item', - ); - - expect(timeWindowDropdown).not.toBeNull(); - expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows); - - done(); - }); - }); - - it('fetches the metrics data with proper time window', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - spyOn(component.$store, 'dispatch').and.stub(); - const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough(); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - - component.$mount(); - - Vue.nextTick() - .then(() => { - expect(component.$store.dispatch).toHaveBeenCalled(); - expect(getTimeDiffSpy).toHaveBeenCalled(); - - done(); - }) - .catch(done.fail); - }); - - it('shows a specific time window selected from the url params', done => { - const start = 1564439536; - const end = 1564441336; - 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, - }); - - setTimeout(() => { - const selectedTimeWindow = component.$el.querySelector('.js-time-window-dropdown .active'); - - expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); - done(); - }); - }); - - it('defaults to the eight hours time window for non valid url parameters', done => { - spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([ - '', - ]); - - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { ...propsData, hasMetrics: true }, - store, - }); - - Vue.nextTick(() => { - expect(component.selectedTimeWindowKey).toEqual(timeWindowsKeyNames.eightHours); - - done(); - }); - }); - }); - - // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922 - // eslint-disable-next-line jasmine/no-disabled-tests - xdescribe('link to chart', () => { - let wrapper; - 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); - - wrapper = shallowMount(DashboardComponent, { - localVue, - sync: false, - attachToDocument: true, - propsData: { ...propsData, hasMetrics: true, currentDashboard }, - store, - }); - - setTimeout(done); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - 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=`); - }); - - 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('when the window resizes', () => { - 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, - }, - store, - }); - - expect(component.elWidth).toEqual(0); - - const pageLayoutEl = document.querySelector('.layout-page'); - pageLayoutEl.classList.add('page-with-icon-sidebar'); - - Vue.nextTick() - .then(() => { - jasmine.clock().tick(1000); - return Vue.nextTick(); - }) - .then(() => { - expect(component.elWidth).toEqual(pageLayoutEl.clientWidth); - done(); - }) - .catch(done.fail); - }); - }); - - describe('external dashboard link', () => { - beforeEach(() => { - mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - showTimeWindowDropdown: false, - externalDashboardUrl: '/mockUrl', - }, - store, - }); - }); - - it('shows the link', done => { - setTimeout(() => { - expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain( - 'View full dashboard', - ); - done(); - }); - }); - }); - - describe('Dashboard dropdown', () => { - beforeEach(() => { - mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - component.$store.dispatch('monitoringDashboard/setFeatureFlags', { - prometheusEndpoint: false, - multipleDashboardsEnabled: true, - }); - - 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, - ); - }); - - it('shows the dashboard dropdown', done => { - setTimeout(() => { - const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown'); - - expect(dashboardDropdown).not.toEqual(null); - done(); - }); - }); - }); - - 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:`); - }); - }); - }); -}); -- cgit v1.2.1