304 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| import { shallowMount } from '@vue/test-utils';
 | |
| import { TEST_HOST } from 'helpers/test_constants';
 | |
| import Anomaly from '~/monitoring/components/charts/anomaly.vue';
 | |
| 
 | |
| import { colorValues } from '~/monitoring/constants';
 | |
| import {
 | |
|   anomalyDeploymentData,
 | |
|   mockProjectDir,
 | |
|   anomalyMockGraphData,
 | |
|   anomalyMockResultValues,
 | |
| } from '../../mock_data';
 | |
| 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 metrics = anomalyMockResultValues[datasetName].map((values, index) => ({
 | |
|     ...template.metrics[index],
 | |
|     result: [
 | |
|       {
 | |
|         metrics: {},
 | |
|         values,
 | |
|       },
 | |
|     ],
 | |
|   }));
 | |
|   return { ...template, metrics };
 | |
| };
 | |
| 
 | |
| 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.metrics.length).toBe(1);
 | |
|         });
 | |
| 
 | |
|         it('receives "metric" with all data', () => {
 | |
|           const { graphData } = getTimeSeriesProps();
 | |
|           const query = graphData.metrics[0];
 | |
|           const expectedQuery = makeAnomalyGraphData(dataSetName).metrics[0];
 | |
|           expect(query).toEqual(expectedQuery);
 | |
|         });
 | |
| 
 | |
|         it('receives the "metric" results', () => {
 | |
|           const { graphData } = getTimeSeriesProps();
 | |
|           const { result } = graphData.metrics[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.metrics.length).toBe(1);
 | |
|         });
 | |
| 
 | |
|         it('receives "metric" results and applies the offset to them', () => {
 | |
|           const { graphData } = getTimeSeriesProps();
 | |
|           const { result } = graphData.metrics[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]);
 | |
|         });
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| });
 |