summaryrefslogtreecommitdiff
path: root/spec/frontend/monitoring
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-30 15:14:17 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-30 15:14:17 +0000
commit3fe9588b1c1c4fb58f8ba8e9c27244fc2fc1c103 (patch)
treed19448d010ff9d58fed14846736ee358fb6b3327 /spec/frontend/monitoring
parentad8eea383406037a207c80421e6e4bfa357f8044 (diff)
downloadgitlab-ce-3fe9588b1c1c4fb58f8ba8e9c27244fc2fc1c103.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/monitoring')
-rw-r--r--spec/frontend/monitoring/components/charts/anomaly_spec.js303
-rw-r--r--spec/frontend/monitoring/mock_data.js161
2 files changed, 464 insertions, 0 deletions
diff --git a/spec/frontend/monitoring/components/charts/anomaly_spec.js b/spec/frontend/monitoring/components/charts/anomaly_spec.js
new file mode 100644
index 00000000000..6707d0b1fe8
--- /dev/null
+++ b/spec/frontend/monitoring/components/charts/anomaly_spec.js
@@ -0,0 +1,303 @@
+import Anomaly from '~/monitoring/components/charts/anomaly.vue';
+
+import { shallowMount } from '@vue/test-utils';
+import { colorValues } from '~/monitoring/constants';
+import {
+ anomalyDeploymentData,
+ mockProjectDir,
+ anomalyMockGraphData,
+ anomalyMockResultValues,
+} from '../../mock_data';
+import { TEST_HOST } from 'helpers/test_constants';
+import MonitorTimeSeriesChart from '~/monitoring/components/charts/time_series.vue';
+
+const mockWidgets = 'mockWidgets';
+const mockProjectPath = `${TEST_HOST}${mockProjectDir}`;
+
+jest.mock('~/lib/utils/icon_utils'); // mock getSvgIconPathContent
+
+const makeAnomalyGraphData = (datasetName, template = anomalyMockGraphData) => {
+ const queries = anomalyMockResultValues[datasetName].map((values, index) => ({
+ ...template.queries[index],
+ result: [
+ {
+ metrics: {},
+ values,
+ },
+ ],
+ }));
+ return { ...template, queries };
+};
+
+describe('Anomaly chart component', () => {
+ let wrapper;
+
+ const setupAnomalyChart = props => {
+ wrapper = shallowMount(Anomaly, {
+ propsData: { ...props },
+ slots: {
+ default: mockWidgets,
+ },
+ sync: false,
+ });
+ };
+ const findTimeSeries = () => wrapper.find(MonitorTimeSeriesChart);
+ const getTimeSeriesProps = () => findTimeSeries().props();
+
+ describe('wrapped monitor-time-series-chart component', () => {
+ const dataSetName = 'noAnomaly';
+ const dataSet = anomalyMockResultValues[dataSetName];
+ const inputThresholds = ['some threshold'];
+
+ beforeEach(() => {
+ setupAnomalyChart({
+ graphData: makeAnomalyGraphData(dataSetName),
+ deploymentData: anomalyDeploymentData,
+ thresholds: inputThresholds,
+ projectPath: mockProjectPath,
+ });
+ });
+
+ it('is a Vue instance', () => {
+ expect(findTimeSeries().exists()).toBe(true);
+ expect(findTimeSeries().isVueInstance()).toBe(true);
+ });
+
+ describe('receives props correctly', () => {
+ describe('graph-data', () => {
+ it('receives a single "metric" series', () => {
+ const { graphData } = getTimeSeriesProps();
+ expect(graphData.queries.length).toBe(1);
+ });
+
+ it('receives "metric" with all data', () => {
+ const { graphData } = getTimeSeriesProps();
+ const query = graphData.queries[0];
+ const expectedQuery = makeAnomalyGraphData(dataSetName).queries[0];
+ expect(query).toEqual(expectedQuery);
+ });
+
+ it('receives the "metric" results', () => {
+ const { graphData } = getTimeSeriesProps();
+ const { result } = graphData.queries[0];
+ const { values } = result[0];
+ const [metricDataset] = dataSet;
+ expect(values).toEqual(expect.any(Array));
+
+ values.forEach(([, y], index) => {
+ expect(y).toBeCloseTo(metricDataset[index][1]);
+ });
+ });
+ });
+
+ describe('option', () => {
+ let option;
+ let series;
+
+ beforeEach(() => {
+ ({ option } = getTimeSeriesProps());
+ ({ series } = option);
+ });
+
+ it('contains a boundary band', () => {
+ expect(series).toEqual(expect.any(Array));
+ expect(series.length).toEqual(2); // 1 upper + 1 lower boundaries
+ expect(series[0].stack).toEqual(series[1].stack);
+
+ series.forEach(s => {
+ expect(s.type).toBe('line');
+ expect(s.lineStyle.width).toBe(0);
+ expect(s.lineStyle.color).toMatch(/rgba\(.+\)/);
+ expect(s.lineStyle.color).toMatch(s.color);
+ expect(s.symbol).toEqual('none');
+ });
+ });
+
+ it('upper boundary values are stacked on top of lower boundary', () => {
+ const [lowerSeries, upperSeries] = series;
+ const [, upperDataset, lowerDataset] = dataSet;
+
+ lowerSeries.data.forEach(([, y], i) => {
+ expect(y).toBeCloseTo(lowerDataset[i][1]);
+ });
+
+ upperSeries.data.forEach(([, y], i) => {
+ expect(y).toBeCloseTo(upperDataset[i][1] - lowerDataset[i][1]);
+ });
+ });
+ });
+
+ describe('series-config', () => {
+ let seriesConfig;
+
+ beforeEach(() => {
+ ({ seriesConfig } = getTimeSeriesProps());
+ });
+
+ it('display symbols is enabled', () => {
+ expect(seriesConfig).toEqual(
+ expect.objectContaining({
+ type: 'line',
+ symbol: 'circle',
+ showSymbol: true,
+ symbolSize: expect.any(Function),
+ itemStyle: {
+ color: expect.any(Function),
+ },
+ }),
+ );
+ });
+ it('does not display anomalies', () => {
+ const { symbolSize, itemStyle } = seriesConfig;
+ const [metricDataset] = dataSet;
+
+ metricDataset.forEach((v, dataIndex) => {
+ const size = symbolSize(null, { dataIndex });
+ const color = itemStyle.color({ dataIndex });
+
+ // normal color and small size
+ expect(size).toBeCloseTo(0);
+ expect(color).toBe(colorValues.primaryColor);
+ });
+ });
+
+ it('can format y values (to use in tooltips)', () => {
+ expect(parseFloat(wrapper.vm.yValueFormatted(0, 0))).toEqual(dataSet[0][0][1]);
+ expect(parseFloat(wrapper.vm.yValueFormatted(1, 0))).toEqual(dataSet[1][0][1]);
+ expect(parseFloat(wrapper.vm.yValueFormatted(2, 0))).toEqual(dataSet[2][0][1]);
+ });
+ });
+
+ describe('inherited properties', () => {
+ it('"deployment-data" keeps the same value', () => {
+ const { deploymentData } = getTimeSeriesProps();
+ expect(deploymentData).toEqual(anomalyDeploymentData);
+ });
+ it('"thresholds" keeps the same value', () => {
+ const { thresholds } = getTimeSeriesProps();
+ expect(thresholds).toEqual(inputThresholds);
+ });
+ it('"projectPath" keeps the same value', () => {
+ const { projectPath } = getTimeSeriesProps();
+ expect(projectPath).toEqual(mockProjectPath);
+ });
+ });
+ });
+ });
+
+ describe('with no boundary data', () => {
+ const dataSetName = 'noBoundary';
+ const dataSet = anomalyMockResultValues[dataSetName];
+
+ beforeEach(() => {
+ setupAnomalyChart({
+ graphData: makeAnomalyGraphData(dataSetName),
+ deploymentData: anomalyDeploymentData,
+ });
+ });
+
+ describe('option', () => {
+ let option;
+ let series;
+
+ beforeEach(() => {
+ ({ option } = getTimeSeriesProps());
+ ({ series } = option);
+ });
+
+ it('does not display a boundary band', () => {
+ expect(series).toEqual(expect.any(Array));
+ expect(series.length).toEqual(0); // no boundaries
+ });
+
+ it('can format y values (to use in tooltips)', () => {
+ expect(parseFloat(wrapper.vm.yValueFormatted(0, 0))).toEqual(dataSet[0][0][1]);
+ expect(wrapper.vm.yValueFormatted(1, 0)).toBe(''); // missing boundary
+ expect(wrapper.vm.yValueFormatted(2, 0)).toBe(''); // missing boundary
+ });
+ });
+ });
+
+ describe('with one anomaly', () => {
+ const dataSetName = 'oneAnomaly';
+ const dataSet = anomalyMockResultValues[dataSetName];
+
+ beforeEach(() => {
+ setupAnomalyChart({
+ graphData: makeAnomalyGraphData(dataSetName),
+ deploymentData: anomalyDeploymentData,
+ });
+ });
+
+ describe('series-config', () => {
+ it('displays one anomaly', () => {
+ const { seriesConfig } = getTimeSeriesProps();
+ const { symbolSize, itemStyle } = seriesConfig;
+ const [metricDataset] = dataSet;
+
+ const bigDots = metricDataset.filter((v, dataIndex) => {
+ const size = symbolSize(null, { dataIndex });
+ return size > 0.1;
+ });
+ const redDots = metricDataset.filter((v, dataIndex) => {
+ const color = itemStyle.color({ dataIndex });
+ return color === colorValues.anomalySymbol;
+ });
+
+ expect(bigDots.length).toBe(1);
+ expect(redDots.length).toBe(1);
+ });
+ });
+ });
+
+ describe('with offset', () => {
+ const dataSetName = 'negativeBoundary';
+ const dataSet = anomalyMockResultValues[dataSetName];
+ const expectedOffset = 4; // Lowst point in mock data is -3.70, it gets rounded
+
+ beforeEach(() => {
+ setupAnomalyChart({
+ graphData: makeAnomalyGraphData(dataSetName),
+ deploymentData: anomalyDeploymentData,
+ });
+ });
+
+ describe('receives props correctly', () => {
+ describe('graph-data', () => {
+ it('receives a single "metric" series', () => {
+ const { graphData } = getTimeSeriesProps();
+ expect(graphData.queries.length).toBe(1);
+ });
+
+ it('receives "metric" results and applies the offset to them', () => {
+ const { graphData } = getTimeSeriesProps();
+ const { result } = graphData.queries[0];
+ const { values } = result[0];
+ const [metricDataset] = dataSet;
+ expect(values).toEqual(expect.any(Array));
+
+ values.forEach(([, y], index) => {
+ expect(y).toBeCloseTo(metricDataset[index][1] + expectedOffset);
+ });
+ });
+ });
+ });
+
+ describe('option', () => {
+ it('upper boundary values are stacked on top of lower boundary, plus the offset', () => {
+ const { option } = getTimeSeriesProps();
+ const { series } = option;
+ const [lowerSeries, upperSeries] = series;
+ const [, upperDataset, lowerDataset] = dataSet;
+
+ lowerSeries.data.forEach(([, y], i) => {
+ expect(y).toBeCloseTo(lowerDataset[i][1] + expectedOffset);
+ });
+
+ upperSeries.data.forEach(([, y], i) => {
+ expect(y).toBeCloseTo(upperDataset[i][1] - lowerDataset[i][1]);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/mock_data.js b/spec/frontend/monitoring/mock_data.js
new file mode 100644
index 00000000000..39b4343d0ce
--- /dev/null
+++ b/spec/frontend/monitoring/mock_data.js
@@ -0,0 +1,161 @@
+export const mockProjectDir = '/frontend-fixtures/environments-project';
+
+export const anomalyDeploymentData = [
+ {
+ id: 111,
+ iid: 3,
+ sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ ref: {
+ name: 'master',
+ },
+ created_at: '2019-08-19T22:00:00.000Z',
+ deployed_at: '2019-08-19T22:01:00.000Z',
+ tag: false,
+ 'last?': true,
+ },
+ {
+ id: 110,
+ iid: 2,
+ sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ ref: {
+ name: 'master',
+ },
+ created_at: '2019-08-19T23:00:00.000Z',
+ deployed_at: '2019-08-19T23:00:00.000Z',
+ tag: false,
+ 'last?': false,
+ },
+];
+
+export const anomalyMockResultValues = {
+ noAnomaly: [
+ [
+ ['2019-08-19T19:00:00.000Z', 1.25],
+ ['2019-08-19T20:00:00.000Z', 1.45],
+ ['2019-08-19T21:00:00.000Z', 1.55],
+ ['2019-08-19T22:00:00.000Z', 1.48],
+ ],
+ [
+ // upper boundary
+ ['2019-08-19T19:00:00.000Z', 2],
+ ['2019-08-19T20:00:00.000Z', 2.55],
+ ['2019-08-19T21:00:00.000Z', 2.65],
+ ['2019-08-19T22:00:00.000Z', 3.0],
+ ],
+ [
+ // lower boundary
+ ['2019-08-19T19:00:00.000Z', 0.45],
+ ['2019-08-19T20:00:00.000Z', 0.65],
+ ['2019-08-19T21:00:00.000Z', 0.7],
+ ['2019-08-19T22:00:00.000Z', 0.8],
+ ],
+ ],
+ noBoundary: [
+ [
+ ['2019-08-19T19:00:00.000Z', 1.25],
+ ['2019-08-19T20:00:00.000Z', 1.45],
+ ['2019-08-19T21:00:00.000Z', 1.55],
+ ['2019-08-19T22:00:00.000Z', 1.48],
+ ],
+ [
+ // empty upper boundary
+ ],
+ [
+ // empty lower boundary
+ ],
+ ],
+ oneAnomaly: [
+ [
+ ['2019-08-19T19:00:00.000Z', 1.25],
+ ['2019-08-19T20:00:00.000Z', 3.45], // anomaly
+ ['2019-08-19T21:00:00.000Z', 1.55],
+ ],
+ [
+ // upper boundary
+ ['2019-08-19T19:00:00.000Z', 2],
+ ['2019-08-19T20:00:00.000Z', 2.55],
+ ['2019-08-19T21:00:00.000Z', 2.65],
+ ],
+ [
+ // lower boundary
+ ['2019-08-19T19:00:00.000Z', 0.45],
+ ['2019-08-19T20:00:00.000Z', 0.65],
+ ['2019-08-19T21:00:00.000Z', 0.7],
+ ],
+ ],
+ negativeBoundary: [
+ [
+ ['2019-08-19T19:00:00.000Z', 1.25],
+ ['2019-08-19T20:00:00.000Z', 3.45], // anomaly
+ ['2019-08-19T21:00:00.000Z', 1.55],
+ ],
+ [
+ // upper boundary
+ ['2019-08-19T19:00:00.000Z', 2],
+ ['2019-08-19T20:00:00.000Z', 2.55],
+ ['2019-08-19T21:00:00.000Z', 2.65],
+ ],
+ [
+ // lower boundary
+ ['2019-08-19T19:00:00.000Z', -1.25],
+ ['2019-08-19T20:00:00.000Z', -2.65],
+ ['2019-08-19T21:00:00.000Z', -3.7], // lowest point
+ ],
+ ],
+};
+
+export const anomalyMockGraphData = {
+ title: 'Requests Per Second Mock Data',
+ type: 'anomaly-chart',
+ weight: 3,
+ metrics: [
+ // Not used
+ ],
+ queries: [
+ {
+ metricId: '90',
+ id: 'metric',
+ query_range: 'MOCK_PROMETHEUS_METRIC_QUERY_RANGE',
+ unit: 'RPS',
+ label: 'Metrics RPS',
+ metric_id: 90,
+ prometheus_endpoint_path: 'MOCK_METRIC_PEP',
+ result: [
+ {
+ metric: {},
+ values: [['2019-08-19T19:00:00.000Z', 0]],
+ },
+ ],
+ },
+ {
+ metricId: '91',
+ id: 'upper',
+ query_range: '...',
+ unit: 'RPS',
+ label: 'Upper Limit Metrics RPS',
+ metric_id: 91,
+ prometheus_endpoint_path: 'MOCK_UPPER_PEP',
+ result: [
+ {
+ metric: {},
+ values: [['2019-08-19T19:00:00.000Z', 0]],
+ },
+ ],
+ },
+ {
+ metricId: '92',
+ id: 'lower',
+ query_range: '...',
+ unit: 'RPS',
+ label: 'Lower Limit Metrics RPS',
+ metric_id: 92,
+ prometheus_endpoint_path: 'MOCK_LOWER_PEP',
+ result: [
+ {
+ metric: {},
+ values: [['2019-08-19T19:00:00.000Z', 0]],
+ },
+ ],
+ },
+ ],
+};