374 lines
10 KiB
JavaScript
374 lines
10 KiB
JavaScript
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';
|
|
import TimeSeriesChart from '~/monitoring/components/charts/time_series.vue';
|
|
import AnomalyChart from '~/monitoring/components/charts/anomaly.vue';
|
|
import { anomalyMockGraphData, graphDataPrometheusQueryRange } from 'jest/monitoring/mock_data';
|
|
import { createStore } from '~/monitoring/stores';
|
|
|
|
global.IS_EE = true;
|
|
global.URL.createObjectURL = jest.fn();
|
|
|
|
const mocks = {
|
|
$toast: {
|
|
show: jest.fn(),
|
|
},
|
|
};
|
|
|
|
describe('Panel Type component', () => {
|
|
let axiosMock;
|
|
let store;
|
|
let state;
|
|
let wrapper;
|
|
|
|
const exampleText = 'example_text';
|
|
|
|
const findCopyLink = () => wrapper.find({ ref: 'copyChartLink' });
|
|
|
|
const createWrapper = props => {
|
|
wrapper = shallowMount(PanelType, {
|
|
propsData: {
|
|
...props,
|
|
},
|
|
store,
|
|
mocks,
|
|
});
|
|
};
|
|
|
|
beforeEach(() => {
|
|
setTestTimeout(1000);
|
|
|
|
store = createStore();
|
|
state = store.state.monitoringDashboard;
|
|
|
|
axiosMock = new AxiosMockAdapter(axios);
|
|
});
|
|
|
|
afterEach(() => {
|
|
axiosMock.reset();
|
|
});
|
|
|
|
describe('When no graphData is available', () => {
|
|
let glEmptyChart;
|
|
// Deep clone object before modifying
|
|
const graphDataNoResult = JSON.parse(JSON.stringify(graphDataPrometheusQueryRange));
|
|
graphDataNoResult.metrics[0].result = [];
|
|
|
|
beforeEach(() => {
|
|
createWrapper({
|
|
graphData: graphDataNoResult,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
wrapper.destroy();
|
|
});
|
|
|
|
describe('Empty Chart component', () => {
|
|
beforeEach(() => {
|
|
glEmptyChart = wrapper.find(EmptyChart);
|
|
});
|
|
|
|
it('renders the chart title', () => {
|
|
expect(wrapper.find({ ref: 'graphTitle' }).text()).toBe(graphDataNoResult.title);
|
|
});
|
|
|
|
it('renders the no download csv link', () => {
|
|
expect(wrapper.find({ ref: 'downloadCsvLink' }).exists()).toBe(false);
|
|
});
|
|
|
|
it('does not contain graph widgets', () => {
|
|
expect(wrapper.find('.js-graph-widgets').exists()).toBe(false);
|
|
});
|
|
|
|
it('is a Vue instance', () => {
|
|
expect(glEmptyChart.isVueInstance()).toBe(true);
|
|
});
|
|
|
|
it('it receives a graph title', () => {
|
|
const props = glEmptyChart.props();
|
|
|
|
expect(props.graphTitle).toBe(wrapper.vm.graphData.title);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when graph data is available', () => {
|
|
beforeEach(() => {
|
|
createWrapper({
|
|
graphData: graphDataPrometheusQueryRange,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
wrapper.destroy();
|
|
});
|
|
|
|
it('renders the chart title', () => {
|
|
expect(wrapper.find({ ref: 'graphTitle' }).text()).toBe(graphDataPrometheusQueryRange.title);
|
|
});
|
|
|
|
it('contains graph widgets', () => {
|
|
expect(wrapper.find('.js-graph-widgets').exists()).toBe(true);
|
|
expect(wrapper.find({ ref: 'downloadCsvLink' }).exists()).toBe(true);
|
|
});
|
|
|
|
it('sets no clipboard copy link on dropdown by default', () => {
|
|
expect(findCopyLink().exists()).toBe(false);
|
|
});
|
|
|
|
describe('Time Series Chart panel type', () => {
|
|
it('is rendered', () => {
|
|
expect(wrapper.find(TimeSeriesChart).isVueInstance()).toBe(true);
|
|
expect(wrapper.find(TimeSeriesChart).exists()).toBe(true);
|
|
});
|
|
|
|
it('includes a default group id', () => {
|
|
expect(wrapper.vm.groupId).toBe('panel-type-chart');
|
|
});
|
|
});
|
|
|
|
describe('Anomaly Chart panel type', () => {
|
|
beforeEach(() => {
|
|
wrapper.setProps({
|
|
graphData: anomalyMockGraphData,
|
|
});
|
|
return wrapper.vm.$nextTick();
|
|
});
|
|
|
|
it('is rendered with an anomaly chart', () => {
|
|
expect(wrapper.find(AnomalyChart).isVueInstance()).toBe(true);
|
|
expect(wrapper.find(AnomalyChart).exists()).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Edit custom metric dropdown item', () => {
|
|
const findEditCustomMetricLink = () => wrapper.find({ ref: 'editMetricLink' });
|
|
|
|
beforeEach(() => {
|
|
createWrapper({
|
|
graphData: {
|
|
...graphDataPrometheusQueryRange,
|
|
},
|
|
});
|
|
|
|
return wrapper.vm.$nextTick();
|
|
});
|
|
|
|
it('is not present if the panel is not a custom metric', () => {
|
|
expect(findEditCustomMetricLink().exists()).toBe(false);
|
|
});
|
|
|
|
it('is present when the panel contains an edit_path property', () => {
|
|
wrapper.setProps({
|
|
graphData: {
|
|
...graphDataPrometheusQueryRange,
|
|
metrics: [
|
|
{
|
|
...graphDataPrometheusQueryRange.metrics[0],
|
|
edit_path: '/root/kubernetes-gke-project/prometheus/metrics/23/edit',
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
return wrapper.vm.$nextTick(() => {
|
|
expect(findEditCustomMetricLink().exists()).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('shows an "Edit metric" link for a panel with a single metric', () => {
|
|
wrapper.setProps({
|
|
graphData: {
|
|
...graphDataPrometheusQueryRange,
|
|
metrics: [
|
|
{
|
|
...graphDataPrometheusQueryRange.metrics[0],
|
|
edit_path: '/root/kubernetes-gke-project/prometheus/metrics/23/edit',
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
return wrapper.vm.$nextTick(() => {
|
|
expect(findEditCustomMetricLink().text()).toBe('Edit metric');
|
|
});
|
|
});
|
|
|
|
it('shows an "Edit metrics" link for a panel with multiple metrics', () => {
|
|
wrapper.setProps({
|
|
graphData: {
|
|
...graphDataPrometheusQueryRange,
|
|
metrics: [
|
|
{
|
|
...graphDataPrometheusQueryRange.metrics[0],
|
|
edit_path: '/root/kubernetes-gke-project/prometheus/metrics/23/edit',
|
|
},
|
|
{
|
|
...graphDataPrometheusQueryRange.metrics[0],
|
|
edit_path: '/root/kubernetes-gke-project/prometheus/metrics/23/edit',
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
return wrapper.vm.$nextTick(() => {
|
|
expect(findEditCustomMetricLink().text()).toBe('Edit metrics');
|
|
});
|
|
});
|
|
});
|
|
|
|
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}`,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when cliboard data is available', () => {
|
|
const clipboardText = 'A value to copy.';
|
|
|
|
beforeEach(() => {
|
|
createWrapper({
|
|
clipboardText,
|
|
graphData: graphDataPrometheusQueryRange,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
wrapper.destroy();
|
|
});
|
|
|
|
it('sets clipboard text on the dropdown', () => {
|
|
expect(findCopyLink().exists()).toBe(true);
|
|
expect(findCopyLink().element.dataset.clipboardText).toBe(clipboardText);
|
|
});
|
|
|
|
it('adds a copy button to the dropdown', () => {
|
|
expect(findCopyLink().text()).toContain('Generate link to chart');
|
|
});
|
|
|
|
it('opens a toast on click', () => {
|
|
findCopyLink().vm.$emit('click');
|
|
|
|
expect(wrapper.vm.$toast.show).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('when downloading metrics data as CSV', () => {
|
|
beforeEach(() => {
|
|
graphDataPrometheusQueryRange.y_label = 'metric';
|
|
wrapper = shallowMount(PanelType, {
|
|
propsData: {
|
|
clipboardText: exampleText,
|
|
graphData: graphDataPrometheusQueryRange,
|
|
},
|
|
store,
|
|
});
|
|
return wrapper.vm.$nextTick();
|
|
});
|
|
|
|
afterEach(() => {
|
|
wrapper.destroy();
|
|
});
|
|
|
|
describe('csvText', () => {
|
|
it('converts metrics data from json to csv', () => {
|
|
const header = `timestamp,${graphDataPrometheusQueryRange.y_label}`;
|
|
const data = graphDataPrometheusQueryRange.metrics[0].result[0].values;
|
|
const firstRow = `${data[0][0]},${data[0][1]}`;
|
|
const secondRow = `${data[1][0]},${data[1][1]}`;
|
|
|
|
expect(wrapper.vm.csvText).toBe(`${header}\r\n${firstRow}\r\n${secondRow}\r\n`);
|
|
});
|
|
});
|
|
|
|
describe('downloadCsv', () => {
|
|
it('produces a link with a Blob', () => {
|
|
expect(global.URL.createObjectURL).toHaveBeenLastCalledWith(expect.any(Blob));
|
|
expect(global.URL.createObjectURL).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
size: wrapper.vm.csvText.length,
|
|
type: 'text/plain',
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|