Commit 236f031d authored by Miguel Rincon's avatar Miguel Rincon

Add a link in dashboard to go to the logs page

- Add time range to the dashboard stores to allow inner components to
create a link to the current time range.
- Have the logs page read time parameters from the URL.
- Add link in panel type component to the logs page.
- Update embed function so logs path is checked for
- Improve and extend specs for panel type
parent bbcd604f
......@@ -21,7 +21,6 @@ import createFlash from '~/flash';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import Icon from '~/vue_shared/components/icon.vue';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
......@@ -102,6 +101,11 @@ export default {
type: String,
required: true,
},
logsPath: {
type: String,
required: false,
default: invalidUrl,
},
defaultBranch: {
type: String,
required: true,
......@@ -247,22 +251,20 @@ export default {
dashboardsEndpoint: this.dashboardsEndpoint,
currentDashboard: this.currentDashboard,
projectPath: this.projectPath,
logsPath: this.logsPath,
});
},
mounted() {
if (!this.hasMetrics) {
this.setGettingStartedEmptyState();
} else {
const { start, end } = convertToFixedRange(this.selectedTimeRange);
this.fetchData({
start,
end,
});
this.setTimeRange(this.selectedTimeRange);
this.fetchData();
}
},
methods: {
...mapActions('monitoringDashboard', [
'setTimeRange',
'fetchData',
'setGettingStartedEmptyState',
'setEndpoints',
......
......@@ -19,14 +19,8 @@ export default {
},
data() {
const timeRange = timeRangeFromUrl(this.dashboardUrl) || defaultTimeRange;
const { start, end } = convertToFixedRange(timeRange);
const params = {
start,
end,
};
return {
params,
timeRange: convertToFixedRange(timeRange),
elWidth: 0,
};
},
......@@ -49,7 +43,9 @@ export default {
},
mounted() {
this.setInitialState();
this.fetchMetricsData(this.params);
this.setTimeRange(this.timeRange);
this.fetchDashboard();
sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
attributes: true,
......@@ -64,7 +60,8 @@ export default {
},
methods: {
...mapActions('monitoringDashboard', [
'fetchMetricsData',
'setTimeRange',
'fetchDashboard',
'setEndpoints',
'setFeatureFlags',
'setShowErrorBanner',
......
<script>
import { mapState } from 'vuex';
import { pickBy } from 'lodash';
import invalidUrl from '~/lib/utils/invalid_url';
import {
GlDropdown,
GlDropdownItem,
......@@ -18,7 +19,7 @@ import MonitorColumnChart from './charts/column.vue';
import MonitorStackedColumnChart from './charts/stacked_column.vue';
import MonitorEmptyChart from './charts/empty_chart.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { downloadCSVOptions, generateLinkToChartOptions } from '../utils';
import { timeRangeToUrl, downloadCSVOptions, generateLinkToChartOptions } from '../utils';
export default {
components: {
......@@ -59,7 +60,7 @@ export default {
},
},
computed: {
...mapState('monitoringDashboard', ['deploymentData', 'projectPath']),
...mapState('monitoringDashboard', ['deploymentData', 'projectPath', 'logsPath', 'timeRange']),
alertWidgetAvailable() {
return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData;
},
......@@ -70,6 +71,12 @@ export default {
this.graphData.metrics[0].result.length > 0
);
},
logsPathWithTimeRange() {
if (this.logsPath && this.logsPath !== invalidUrl && this.timeRange) {
return timeRangeToUrl(this.timeRange, this.logsPath);
}
return null;
},
csvText() {
const chartData = this.graphData.metrics[0].result[0].values;
const yLabel = this.graphData.y_label;
......@@ -157,6 +164,15 @@ export default {
<template slot="button-content">
<icon name="ellipsis_v" class="text-secondary" />
</template>
<gl-dropdown-item
v-if="logsPathWithTimeRange"
ref="viewLogsLink"
:href="logsPathWithTimeRange"
>
{{ s__('Metrics|View logs') }}
</gl-dropdown-item>
<gl-dropdown-item
v-track-event="downloadCSVOptions(graphData.title)"
:href="downloadCsv"
......
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { gqClient, parseEnvironmentsResponse, removeLeadingSlash } from './utils';
import trackDashboardLoad from '../monitoring_tracking_helper';
import getEnvironments from '../queries/getEnvironments.query.graphql';
......@@ -32,6 +33,10 @@ export const setEndpoints = ({ commit }, endpoints) => {
commit(types.SET_ENDPOINTS, endpoints);
};
export const setTimeRange = ({ commit }, timeRange) => {
commit(types.SET_TIME_RANGE, timeRange);
};
export const filterEnvironments = ({ commit, dispatch }, searchTerm) => {
commit(types.SET_ENVIRONMENTS_FILTER, searchTerm);
dispatch('fetchEnvironmentsData');
......@@ -63,19 +68,24 @@ export const receiveEnvironmentsDataSuccess = ({ commit }, data) =>
export const receiveEnvironmentsDataFailure = ({ commit }) =>
commit(types.RECEIVE_ENVIRONMENTS_DATA_FAILURE);
export const fetchData = ({ dispatch }, params) => {
dispatch('fetchMetricsData', params);
export const fetchData = ({ dispatch }) => {
dispatch('fetchDashboard');
dispatch('fetchDeploymentsData');
dispatch('fetchEnvironmentsData');
};
export const fetchMetricsData = ({ dispatch }, params) => dispatch('fetchDashboard', params);
export const fetchDashboard = ({ state, dispatch }, params) => {
export const fetchDashboard = ({ state, dispatch }) => {
dispatch('requestMetricsDashboard');
const params = {};
if (state.timeRange) {
const { start, end } = convertToFixedRange(state.timeRange);
params.start = start;
params.end = end;
}
if (state.currentDashboard) {
// eslint-disable-next-line no-param-reassign
params.dashboard = state.currentDashboard;
}
......
......@@ -14,7 +14,7 @@ export const REQUEST_METRIC_RESULT = 'REQUEST_METRIC_RESULT';
export const RECEIVE_METRIC_RESULT_SUCCESS = 'RECEIVE_METRIC_RESULT_SUCCESS';
export const RECEIVE_METRIC_RESULT_FAILURE = 'RECEIVE_METRIC_RESULT_FAILURE';
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
export const SET_TIME_RANGE = 'SET_TIME_RANGE';
export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
......
......@@ -182,6 +182,10 @@ export default {
state.dashboardsEndpoint = endpoints.dashboardsEndpoint;
state.currentDashboard = endpoints.currentDashboard;
state.projectPath = endpoints.projectPath;
state.logsPath = endpoints.logsPath || state.logsPath;
},
[types.SET_TIME_RANGE](state, timeRange) {
state.timeRange = timeRange;
},
[types.SET_GETTING_STARTED_EMPTY_STATE](state) {
state.emptyState = 'gettingStarted';
......
import invalidUrl from '~/lib/utils/invalid_url';
export default () => ({
// API endpoints
metricsEndpoint: null,
deploymentsEndpoint: null,
dashboardEndpoint: invalidUrl,
// Dashboard request parameters
timeRange: null,
currentDashboard: null,
// Dashboard data
emptyState: 'gettingStarted',
showEmptyState: true,
showErrorBanner: true,
dashboard: {
panel_groups: [],
},
allDashboards: [],
// Other project data
deploymentData: [],
environments: [],
environmentsSearchTerm: '',
environmentsLoading: false,
allDashboards: [],
currentDashboard: null,
// GitLab paths to other pages
projectPath: null,
logsPath: invalidUrl,
});
......@@ -103,8 +103,9 @@ export const graphDataValidatorForAnomalyValues = graphData => {
/**
* Returns a time range from the current URL params
*
* @returns {Object} The time range defined by the
* current URL, reading from `window.location.search`
* @returns {Object|null} The time range defined by the
* current URL, reading from search query or `window.location.search`.
* Returns `null` if no parameters form a time range.
*/
export const timeRangeFromUrl = (search = window.location.search) => {
const params = queryToObject(search);
......
......@@ -6,6 +6,7 @@ import { scrollDown } from '~/lib/utils/scroll_utils';
import LogControlButtons from './log_control_buttons.vue';
import { timeRanges, defaultTimeRange } from '~/monitoring/constants';
import { timeRangeFromUrl } from '~/monitoring/utils';
export default {
components: {
......@@ -41,7 +42,6 @@ export default {
data() {
return {
searchQuery: '',
selectedTimeRange: defaultTimeRange,
timeRanges,
isElasticStackCalloutDismissed: false,
};
......@@ -91,6 +91,7 @@ export default {
},
mounted() {
this.setInitData({
timeRange: timeRangeFromUrl() || defaultTimeRange,
environmentName: this.environmentName,
podName: this.currentPodName,
});
......
......@@ -31,7 +31,10 @@ const requestLogsUntilData = params =>
});
});
export const setInitData = ({ commit }, { environmentName, podName }) => {
export const setInitData = ({ commit }, { timeRange, environmentName, podName }) => {
if (timeRange) {
commit(types.SET_TIME_RANGE, timeRange);
}
commit(types.SET_PROJECT_ENVIRONMENT, environmentName);
commit(types.SET_CURRENT_POD_NAME, podName);
};
......
---
title: Add a link in dashboard to allow users to go to the logs page
merge_request: 24240
author:
type: added
......@@ -131,6 +131,9 @@ describe('EnvironmentLogs', () => {
expect(actionMocks.setInitData).toHaveBeenCalledTimes(1);
expect(actionMocks.setInitData).toHaveBeenLastCalledWith({
timeRange: expect.objectContaining({
default: true,
}),
environmentName: mockEnvName,
podName: null,
});
......
......@@ -17,7 +17,6 @@ describe('Panel Type', () => {
let axiosMock;
let panelType;
let store;
const dashboardWidth = 100;
const exampleText = 'example_text';
const createWrapper = propsData => {
......@@ -46,7 +45,6 @@ describe('Panel Type', () => {
beforeEach(() => {
createWrapper({
clipboardText: exampleText,
dashboardWidth,
graphData: graphDataPrometheusQueryRange,
alertsEndpoint: '/endpoint',
prometheusAlertsAvailable: true,
......@@ -89,7 +87,6 @@ describe('Panel Type', () => {
beforeEach(() => {
createWrapper({
clipboardText: exampleText,
dashboardWidth,
graphData: graphDataPrometheusQueryRange,
alertsEndpoint: '/endpoint',
prometheusAlertsAvailable: false,
......
......@@ -12118,6 +12118,9 @@ msgstr ""
msgid "Metrics|Validating query"
msgstr ""
msgid "Metrics|View logs"
msgstr ""
msgid "Metrics|Y-axis label"
msgstr ""
......
......@@ -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(() => {
return wrapper.vm.$nextTick().then(() => {
expect(store.dispatch).toHaveBeenCalled();
done();
})
.catch(done.fail);
});
});
});
......@@ -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);
});
......
......@@ -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);
});
......
......@@ -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);
......
......@@ -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,
......
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,105 @@ 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 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(() => {
expect(findViewLogsLink().exists()).toBe(true);
expect(findViewLogsLink().attributes('href')).toEqual('/path/to/logs?duration_seconds=120');
});
});
});
......@@ -119,20 +178,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 +197,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 +220,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 +229,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',
}),
);
......
......@@ -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';
......
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