diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-07 12:09:13 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-07 12:09:13 +0000 |
commit | 211a8c3361ccf4eb92f36edbdcf15c98fcdcc8b7 (patch) | |
tree | 0ad37172721a39b0d57240bb1b4e70f200a0d93e /spec/frontend/monitoring | |
parent | 456a7247f9e88fc2518b69a1a00e905c6db6d775 (diff) | |
download | gitlab-ce-211a8c3361ccf4eb92f36edbdcf15c98fcdcc8b7.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/monitoring')
-rw-r--r-- | spec/frontend/monitoring/components/charts/time_series_spec.js | 59 | ||||
-rw-r--r-- | spec/frontend/monitoring/components/dashboard_spec.js | 40 | ||||
-rw-r--r-- | spec/frontend/monitoring/components/dashboard_url_time_spec.js | 59 | ||||
-rw-r--r-- | spec/frontend/monitoring/embed/embed_spec.js | 21 | ||||
-rw-r--r-- | spec/frontend/monitoring/init_utils.js | 1 | ||||
-rw-r--r-- | spec/frontend/monitoring/panel_type_spec.js | 146 | ||||
-rw-r--r-- | spec/frontend/monitoring/store/mutations_spec.js | 10 |
7 files changed, 252 insertions, 84 deletions
diff --git a/spec/frontend/monitoring/components/charts/time_series_spec.js b/spec/frontend/monitoring/components/charts/time_series_spec.js index d4f197a708f..0a7e3dca183 100644 --- a/spec/frontend/monitoring/components/charts/time_series_spec.js +++ b/spec/frontend/monitoring/components/charts/time_series_spec.js @@ -18,6 +18,15 @@ import * as iconUtils from '~/lib/utils/icon_utils'; const mockWidgets = 'mockWidgets'; const mockSvgPathContent = 'mockSvgPathContent'; + +jest.mock('lodash/throttle', () => + // this throttle mock executes immediately + jest.fn(func => { + // eslint-disable-next-line no-param-reassign + func.cancel = jest.fn(); + return func; + }), +); jest.mock('~/lib/utils/icon_utils', () => ({ getSvgIconPathContent: jest.fn().mockImplementation(() => Promise.resolve(mockSvgPathContent)), })); @@ -94,6 +103,56 @@ describe('Time series component', () => { }); }); + describe('events', () => { + describe('datazoom', () => { + let eChartMock; + let startValue; + let endValue; + + const findChart = () => timeSeriesChart.find({ ref: 'chart' }); + + beforeEach(done => { + eChartMock = { + handlers: {}, + getOption: () => ({ + dataZoom: [ + { + startValue, + endValue, + }, + ], + }), + off: jest.fn(eChartEvent => { + delete eChartMock.handlers[eChartEvent]; + }), + on: jest.fn((eChartEvent, fn) => { + eChartMock.handlers[eChartEvent] = fn; + }), + }; + + timeSeriesChart = makeTimeSeriesChart(mockGraphData); + timeSeriesChart.vm.$nextTick(() => { + findChart().vm.$emit('created', eChartMock); + done(); + }); + }); + + it('handles datazoom event from chart', () => { + startValue = 1577836800000; // 2020-01-01T00:00:00.000Z + endValue = 1577840400000; // 2020-01-01T01:00:00.000Z + eChartMock.handlers.datazoom(); + + expect(timeSeriesChart.emitted('datazoom')).toHaveLength(1); + expect(timeSeriesChart.emitted('datazoom')[0]).toEqual([ + { + start: new Date(startValue).toISOString(), + end: new Date(endValue).toISOString(), + }, + ]); + }); + }); + }); + describe('methods', () => { describe('formatTooltipText', () => { let mockDate; diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js index 31266b4f6d4..70b2c9cf527 100644 --- a/spec/frontend/monitoring/components/dashboard_spec.js +++ b/spec/frontend/monitoring/components/dashboard_spec.js @@ -73,12 +73,20 @@ describe('Dashboard', () => { describe('no metrics are available yet', () => { beforeEach(() => { + jest.spyOn(store, 'dispatch'); createShallowWrapper(); }); it('shows the environment selector', () => { expect(findEnvironmentsDropdown().exists()).toBe(true); }); + + it('sets endpoints: logs path', () => { + expect(store.dispatch).toHaveBeenCalledWith( + 'monitoringDashboard/setEndpoints', + expect.objectContaining({ logsPath: propsData.logsPath }), + ); + }); }); describe('no data found', () => { @@ -94,6 +102,21 @@ describe('Dashboard', () => { }); describe('request information to the server', () => { + it('calls to set time range and fetch data', () => { + jest.spyOn(store, 'dispatch'); + + createShallowWrapper({ hasMetrics: true }, { methods: {} }); + + return wrapper.vm.$nextTick().then(() => { + expect(store.dispatch).toHaveBeenCalledWith( + 'monitoringDashboard/setTimeRange', + expect.any(Object), + ); + + expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', undefined); + }); + }); + it('shows up a loading state', done => { createShallowWrapper({ hasMetrics: true }, { methods: {} }); @@ -126,7 +149,7 @@ describe('Dashboard', () => { .catch(done.fail); }); - it('fetches the metrics data with proper time window', done => { + it('fetches the metrics data with proper time window', () => { jest.spyOn(store, 'dispatch'); createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); @@ -136,14 +159,9 @@ describe('Dashboard', () => { environmentData, ); - wrapper.vm - .$nextTick() - .then(() => { - expect(store.dispatch).toHaveBeenCalled(); - - done(); - }) - .catch(done.fail); + return wrapper.vm.$nextTick().then(() => { + expect(store.dispatch).toHaveBeenCalled(); + }); }); }); @@ -263,10 +281,6 @@ describe('Dashboard', () => { return wrapper.vm.$nextTick(); }); - afterEach(() => { - wrapper.destroy(); - }); - it('renders a search input', () => { expect(wrapper.find({ ref: 'monitorEnvironmentsDropdownSearch' }).exists()).toBe(true); }); diff --git a/spec/frontend/monitoring/components/dashboard_url_time_spec.js b/spec/frontend/monitoring/components/dashboard_url_time_spec.js index 33fbfac486f..161c64dd74b 100644 --- a/spec/frontend/monitoring/components/dashboard_url_time_spec.js +++ b/spec/frontend/monitoring/components/dashboard_url_time_spec.js @@ -7,6 +7,7 @@ import { mockProjectDir } from '../mock_data'; import Dashboard from '~/monitoring/components/dashboard.vue'; import { createStore } from '~/monitoring/stores'; +import { defaultTimeRange } from '~/monitoring/constants'; import { propsData } from '../init_utils'; jest.mock('~/flash'); @@ -17,16 +18,11 @@ describe('dashboard invalid url parameters', () => { let wrapper; let mock; - const fetchDataMock = jest.fn(); - const createMountedWrapper = (props = { hasMetrics: true }, options = {}) => { wrapper = mount(Dashboard, { propsData: { ...propsData, ...props }, store, stubs: ['graph-group', 'panel-type'], - methods: { - fetchData: fetchDataMock, - }, ...options, }); }; @@ -35,6 +31,8 @@ describe('dashboard invalid url parameters', () => { beforeEach(() => { store = createStore(); + jest.spyOn(store, 'dispatch'); + mock = new MockAdapter(axios); }); @@ -43,7 +41,6 @@ describe('dashboard invalid url parameters', () => { wrapper.destroy(); } mock.restore(); - fetchDataMock.mockReset(); queryToObject.mockReset(); }); @@ -53,15 +50,13 @@ describe('dashboard invalid url parameters', () => { createMountedWrapper(); return wrapper.vm.$nextTick().then(() => { - expect(findDateTimePicker().props('value')).toMatchObject({ - duration: { seconds: 28800 }, - }); + expect(findDateTimePicker().props('value')).toEqual(defaultTimeRange); - expect(fetchDataMock).toHaveBeenCalledTimes(1); - expect(fetchDataMock).toHaveBeenCalledWith({ - start: expect.any(String), - end: expect.any(String), - }); + expect(store.dispatch).toHaveBeenCalledWith( + 'monitoringDashboard/setTimeRange', + expect.any(Object), + ); + expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', undefined); }); }); @@ -78,8 +73,8 @@ describe('dashboard invalid url parameters', () => { return wrapper.vm.$nextTick().then(() => { expect(findDateTimePicker().props('value')).toEqual(params); - expect(fetchDataMock).toHaveBeenCalledTimes(1); - expect(fetchDataMock).toHaveBeenCalledWith(params); + expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/setTimeRange', params); + expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', undefined); }); }); @@ -91,15 +86,17 @@ describe('dashboard invalid url parameters', () => { createMountedWrapper(); return wrapper.vm.$nextTick().then(() => { - expect(findDateTimePicker().props('value')).toMatchObject({ + const expectedTimeRange = { duration: { seconds: 60 * 2 }, - }); + }; - expect(fetchDataMock).toHaveBeenCalledTimes(1); - expect(fetchDataMock).toHaveBeenCalledWith({ - start: expect.any(String), - end: expect.any(String), - }); + expect(findDateTimePicker().props('value')).toMatchObject(expectedTimeRange); + + expect(store.dispatch).toHaveBeenCalledWith( + 'monitoringDashboard/setTimeRange', + expectedTimeRange, + ); + expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', undefined); }); }); @@ -114,15 +111,13 @@ describe('dashboard invalid url parameters', () => { return wrapper.vm.$nextTick().then(() => { expect(createFlash).toHaveBeenCalled(); - expect(findDateTimePicker().props('value')).toMatchObject({ - duration: { seconds: 28800 }, - }); + expect(findDateTimePicker().props('value')).toEqual(defaultTimeRange); - expect(fetchDataMock).toHaveBeenCalledTimes(1); - expect(fetchDataMock).toHaveBeenCalledWith({ - start: expect.any(String), - end: expect.any(String), - }); + expect(store.dispatch).toHaveBeenCalledWith( + 'monitoringDashboard/setTimeRange', + defaultTimeRange, + ); + expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', undefined); }); }); @@ -137,7 +132,7 @@ describe('dashboard invalid url parameters', () => { duration: { seconds: 120 }, }); - // redirect to plus + new parameters + // redirect to with new parameters expect(mergeUrlParams).toHaveBeenCalledWith({ duration_seconds: '120' }, toUrl); expect(redirectTo).toHaveBeenCalledTimes(1); }); diff --git a/spec/frontend/monitoring/embed/embed_spec.js b/spec/frontend/monitoring/embed/embed_spec.js index 831ab1ed157..3bb70a02bd9 100644 --- a/spec/frontend/monitoring/embed/embed_spec.js +++ b/spec/frontend/monitoring/embed/embed_spec.js @@ -26,10 +26,11 @@ describe('Embed', () => { beforeEach(() => { actions = { - setFeatureFlags: () => {}, - setShowErrorBanner: () => {}, - setEndpoints: () => {}, - fetchMetricsData: () => {}, + setFeatureFlags: jest.fn(), + setShowErrorBanner: jest.fn(), + setEndpoints: jest.fn(), + setTimeRange: jest.fn(), + fetchDashboard: jest.fn(), }; metricsWithDataGetter = jest.fn(); @@ -76,6 +77,18 @@ describe('Embed', () => { mountComponent(); }); + it('calls actions to fetch data', () => { + const expectedTimeRangePayload = expect.objectContaining({ + start: expect.any(String), + end: expect.any(String), + }); + + expect(actions.setTimeRange).toHaveBeenCalledTimes(1); + expect(actions.setTimeRange.mock.calls[0][1]).toEqual(expectedTimeRangePayload); + + expect(actions.fetchDashboard).toHaveBeenCalled(); + }); + it('shows a chart when metrics are present', () => { expect(wrapper.find('.metrics-embed').exists()).toBe(true); expect(wrapper.find(PanelType).exists()).toBe(true); diff --git a/spec/frontend/monitoring/init_utils.js b/spec/frontend/monitoring/init_utils.js index 30c64a8d885..36c654ba7b3 100644 --- a/spec/frontend/monitoring/init_utils.js +++ b/spec/frontend/monitoring/init_utils.js @@ -15,6 +15,7 @@ export const propsData = { clustersPath: '/path/to/clusters', tagsPath: '/path/to/tags', projectPath: '/path/to/project', + logsPath: '/path/to/logs', defaultBranch: 'master', metricsEndpoint: mockApiEndpoint, deploymentsEndpoint: null, diff --git a/spec/frontend/monitoring/panel_type_spec.js b/spec/frontend/monitoring/panel_type_spec.js index e51b69ef14d..730d67f79d8 100644 --- a/spec/frontend/monitoring/panel_type_spec.js +++ b/spec/frontend/monitoring/panel_type_spec.js @@ -1,6 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import AxiosMockAdapter from 'axios-mock-adapter'; import { setTestTimeout } from 'helpers/timeout'; +import invalidUrl from '~/lib/utils/invalid_url'; import axios from '~/lib/utils/axios_utils'; import PanelType from '~/monitoring/components/panel_type.vue'; import EmptyChart from '~/monitoring/components/charts/empty_chart.vue'; @@ -16,20 +17,25 @@ global.URL.createObjectURL = jest.fn(); describe('Panel Type component', () => { let axiosMock; let store; - let panelType; - const dashboardWidth = 100; + let state; + let wrapper; const exampleText = 'example_text'; - const createWrapper = props => - shallowMount(PanelType, { + const createWrapper = props => { + wrapper = shallowMount(PanelType, { propsData: { ...props, }, store, }); + }; beforeEach(() => { setTestTimeout(1000); + + store = createStore(); + state = store.state.monitoringDashboard; + axiosMock = new AxiosMockAdapter(axios); }); @@ -44,19 +50,18 @@ describe('Panel Type component', () => { graphDataNoResult.metrics[0].result = []; beforeEach(() => { - panelType = createWrapper({ - dashboardWidth, + createWrapper({ graphData: graphDataNoResult, }); }); afterEach(() => { - panelType.destroy(); + wrapper.destroy(); }); describe('Empty Chart component', () => { beforeEach(() => { - glEmptyChart = panelType.find(EmptyChart); + glEmptyChart = wrapper.find(EmptyChart); }); it('is a Vue instance', () => { @@ -66,51 +71,126 @@ describe('Panel Type component', () => { it('it receives a graph title', () => { const props = glEmptyChart.props(); - expect(props.graphTitle).toBe(panelType.vm.graphData.title); + expect(props.graphTitle).toBe(wrapper.vm.graphData.title); }); }); }); describe('when graph data is available', () => { beforeEach(() => { - store = createStore(); - panelType = createWrapper({ - dashboardWidth, + createWrapper({ graphData: graphDataPrometheusQueryRange, }); }); afterEach(() => { - panelType.destroy(); + wrapper.destroy(); }); it('sets no clipboard copy link on dropdown by default', () => { - const link = () => panelType.find('.js-chart-link'); + const link = () => wrapper.find('.js-chart-link'); expect(link().exists()).toBe(false); }); describe('Time Series Chart panel type', () => { it('is rendered', () => { - expect(panelType.find(TimeSeriesChart).isVueInstance()).toBe(true); - expect(panelType.find(TimeSeriesChart).exists()).toBe(true); + expect(wrapper.find(TimeSeriesChart).isVueInstance()).toBe(true); + expect(wrapper.find(TimeSeriesChart).exists()).toBe(true); }); it('includes a default group id', () => { - expect(panelType.vm.groupId).toBe('panel-type-chart'); + expect(wrapper.vm.groupId).toBe('panel-type-chart'); }); }); describe('Anomaly Chart panel type', () => { - beforeEach(done => { - panelType.setProps({ + beforeEach(() => { + wrapper.setProps({ graphData: anomalyMockGraphData, }); - panelType.vm.$nextTick(done); + return wrapper.vm.$nextTick(); }); it('is rendered with an anomaly chart', () => { - expect(panelType.find(AnomalyChart).isVueInstance()).toBe(true); - expect(panelType.find(AnomalyChart).exists()).toBe(true); + expect(wrapper.find(AnomalyChart).isVueInstance()).toBe(true); + expect(wrapper.find(AnomalyChart).exists()).toBe(true); + }); + }); + }); + + describe('View Logs dropdown item', () => { + const mockLogsPath = '/path/to/logs'; + const mockTimeRange = { duration: { seconds: 120 } }; + + const findTimeChart = () => wrapper.find({ ref: 'timeChart' }); + const findViewLogsLink = () => wrapper.find({ ref: 'viewLogsLink' }); + + beforeEach(() => { + createWrapper({ + graphData: graphDataPrometheusQueryRange, + }); + return wrapper.vm.$nextTick(); + }); + + it('is not present by default', () => + wrapper.vm.$nextTick(() => { + expect(findViewLogsLink().exists()).toBe(false); + })); + + it('is not present if a time range is not set', () => { + state.logsPath = mockLogsPath; + state.timeRange = null; + + return wrapper.vm.$nextTick(() => { + expect(findViewLogsLink().exists()).toBe(false); + }); + }); + + it('is not present if the logs path is default', () => { + state.logsPath = invalidUrl; + state.timeRange = mockTimeRange; + + return wrapper.vm.$nextTick(() => { + expect(findViewLogsLink().exists()).toBe(false); + }); + }); + + it('is not present if the logs path is not set', () => { + state.logsPath = null; + state.timeRange = mockTimeRange; + + return wrapper.vm.$nextTick(() => { + expect(findViewLogsLink().exists()).toBe(false); + }); + }); + + it('is present when logs path and time a range is present', () => { + state.logsPath = mockLogsPath; + state.timeRange = mockTimeRange; + + return wrapper.vm.$nextTick(() => { + const href = `${mockLogsPath}?duration_seconds=${mockTimeRange.duration.seconds}`; + expect(findViewLogsLink().attributes('href')).toMatch(href); + }); + }); + + it('it is overriden when a datazoom event is received', () => { + state.logsPath = mockLogsPath; + state.timeRange = mockTimeRange; + + const zoomedTimeRange = { + start: '2020-01-01T00:00:00.000Z', + end: '2020-01-01T01:00:00.000Z', + }; + + findTimeChart().vm.$emit('datazoom', zoomedTimeRange); + + return wrapper.vm.$nextTick(() => { + const start = encodeURIComponent(zoomedTimeRange.start); + const end = encodeURIComponent(zoomedTimeRange.end); + expect(findViewLogsLink().attributes('href')).toMatch( + `${mockLogsPath}?start=${start}&end=${end}`, + ); }); }); }); @@ -119,20 +199,18 @@ describe('Panel Type component', () => { const clipboardText = 'A value to copy.'; beforeEach(() => { - store = createStore(); - panelType = createWrapper({ + createWrapper({ clipboardText, - dashboardWidth, graphData: graphDataPrometheusQueryRange, }); }); afterEach(() => { - panelType.destroy(); + wrapper.destroy(); }); it('sets clipboard text on the dropdown', () => { - const link = () => panelType.find('.js-chart-link'); + const link = () => wrapper.find('.js-chart-link'); expect(link().exists()).toBe(true); expect(link().element.dataset.clipboardText).toBe(clipboardText); @@ -140,22 +218,20 @@ describe('Panel Type component', () => { }); describe('when downloading metrics data as CSV', () => { - beforeEach(done => { + beforeEach(() => { graphDataPrometheusQueryRange.y_label = 'metric'; - store = createStore(); - panelType = shallowMount(PanelType, { + wrapper = shallowMount(PanelType, { propsData: { clipboardText: exampleText, - dashboardWidth, graphData: graphDataPrometheusQueryRange, }, store, }); - panelType.vm.$nextTick(done); + return wrapper.vm.$nextTick(); }); afterEach(() => { - panelType.destroy(); + wrapper.destroy(); }); describe('csvText', () => { @@ -165,7 +241,7 @@ describe('Panel Type component', () => { const firstRow = `${data[0][0]},${data[0][1]}`; const secondRow = `${data[1][0]},${data[1][1]}`; - expect(panelType.vm.csvText).toBe(`${header}\r\n${firstRow}\r\n${secondRow}\r\n`); + expect(wrapper.vm.csvText).toBe(`${header}\r\n${firstRow}\r\n${secondRow}\r\n`); }); }); @@ -174,7 +250,7 @@ describe('Panel Type component', () => { expect(global.URL.createObjectURL).toHaveBeenLastCalledWith(expect.any(Blob)); expect(global.URL.createObjectURL).toHaveBeenLastCalledWith( expect.objectContaining({ - size: panelType.vm.csvText.length, + size: wrapper.vm.csvText.length, type: 'text/plain', }), ); diff --git a/spec/frontend/monitoring/store/mutations_spec.js b/spec/frontend/monitoring/store/mutations_spec.js index 7f8ced8cf43..b0ac42e0e5f 100644 --- a/spec/frontend/monitoring/store/mutations_spec.js +++ b/spec/frontend/monitoring/store/mutations_spec.js @@ -90,6 +90,16 @@ describe('Monitoring mutations', () => { expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json'); expect(stateCopy.projectPath).toEqual('/gitlab-org/gitlab-foss'); }); + + it('should not remove default value of logsPath', () => { + const defaultLogsPath = stateCopy.logsPath; + + mutations[types.SET_ENDPOINTS](stateCopy, { + dashboardEndpoint: 'dashboard.json', + }); + + expect(stateCopy.logsPath).toBe(defaultLogsPath); + }); }); describe('Individual panel/metric results', () => { const metricId = '12_system_metrics_kubernetes_container_memory_total'; |