Commit 657fea86 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Add summary statistics prometheus dashboard

parent 9e3cdc02
......@@ -3,6 +3,7 @@ import { scaleLinear, scaleTime } from 'd3-scale';
import { axisLeft, axisBottom } from 'd3-axis';
import { max, extent } from 'd3-array';
import { select } from 'd3-selection';
import GraphAxis from './graph/axis.vue';
import GraphLegend from './graph/legend.vue';
import GraphFlag from './graph/flag.vue';
import GraphDeployment from './graph/deployment.vue';
......@@ -18,10 +19,11 @@ const d3 = { scaleLinear, scaleTime, axisLeft, axisBottom, max, extent, select }
export default {
components: {
GraphLegend,
GraphAxis,
GraphFlag,
GraphDeployment,
GraphPath,
GraphLegend,
},
mixins: [MonitoringMixin],
props: {
......@@ -138,7 +140,7 @@ export default {
this.legendTitle = query.label || 'Average';
this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right;
this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
this.baseGraphHeight = this.graphHeight;
this.baseGraphHeight = this.graphHeight - 50;
this.baseGraphWidth = this.graphWidth;
// pixel offsets inside the svg and outside are not 1:1
......@@ -177,14 +179,10 @@ export default {
this.graphHeightOffset,
);
if (!this.showLegend) {
this.baseGraphHeight -= 50;
} else if (this.timeSeries.length > 3) {
this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20;
}
const axisXScale = d3.scaleTime().range([0, this.graphWidth - 70]);
const axisYScale = d3.scaleLinear().range([this.graphHeight - this.graphHeightOffset, 0]);
const axisXScale = d3.scaleTime()
.range([0, this.graphWidth - 70]);
const axisYScale = d3.scaleLinear()
.range([this.graphHeight - this.graphHeightOffset, 0]);
const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []);
axisXScale.domain(d3.extent(allValues, d => d.time));
......@@ -251,17 +249,12 @@ export default {
class="y-axis"
transform="translate(70, 20)"
/>
<graph-legend
<graph-axis
:graph-width="graphWidth"
:graph-height="graphHeight"
:margin="margin"
:measurements="measurements"
:legend-title="legendTitle"
:y-axis-label="yAxisLabel"
:time-series="timeSeries"
:unit-of-display="unitOfDisplay"
:current-data-index="currentDataIndex"
:show-legend-group="showLegend"
/>
<svg
class="graph-data"
......@@ -306,5 +299,12 @@ export default {
:deployment-flag-data="deploymentFlagData"
/>
</div>
<graph-legend
v-if="showLegend"
:legend-title="legendTitle"
:time-series="timeSeries"
:current-data-index="currentDataIndex"
:unit-of-display="unitOfDisplay"
/>
</div>
</template>
<script>
export default {
props: {
graphWidth: {
type: Number,
required: true,
},
graphHeight: {
type: Number,
required: true,
},
margin: {
type: Object,
required: true,
},
measurements: {
type: Object,
required: true,
},
yAxisLabel: {
type: String,
required: true,
},
},
data() {
return {
yLabelWidth: 0,
yLabelHeight: 0,
};
},
computed: {
textTransform() {
const yCoordinate =
(this.graphHeight -
this.margin.top +
this.measurements.axisLabelLineOffset) /
2 || 0;
return `translate(15, ${yCoordinate}) rotate(-90)`;
},
rectTransform() {
const yCoordinate =
(this.graphHeight -
this.margin.top +
this.measurements.axisLabelLineOffset) /
2 +
this.yLabelWidth / 2 || 0;
return `translate(0, ${yCoordinate}) rotate(-90)`;
},
xPosition() {
return (
(this.graphWidth + this.measurements.axisLabelLineOffset) / 2 -
this.margin.right || 0
);
},
yPosition() {
return (
this.graphHeight -
this.margin.top +
this.measurements.axisLabelLineOffset || 0
);
},
},
mounted() {
this.$nextTick(() => {
const bbox = this.$refs.ylabel.getBBox();
this.yLabelWidth = bbox.width + 10; // Added some padding
this.yLabelHeight = bbox.height + 5;
});
},
};
</script>
<template>
<g class="axis-label-container">
<line
class="label-x-axis-line"
stroke="#000000"
stroke-width="1"
x1="10"
:y1="yPosition"
:x2="graphWidth + 20"
:y2="yPosition"
/>
<line
class="label-y-axis-line"
stroke="#000000"
stroke-width="1"
x1="10"
y1="0"
:x2="10"
:y2="yPosition"
/>
<rect
class="rect-axis-text"
:transform="rectTransform"
:width="yLabelWidth"
:height="yLabelHeight"
/>
<text
class="label-axis-text y-label-text"
text-anchor="middle"
:transform="textTransform"
ref="ylabel"
>
{{ yAxisLabel }}
</text>
<rect
class="rect-axis-text"
:x="xPosition + 60"
:y="graphHeight - 80"
width="35"
height="50"
/>
<text
class="label-axis-text x-label-text"
:x="xPosition + 60"
:y="yPosition"
dy=".35em"
>
Time
</text>
</g>
</template>
......@@ -160,7 +160,7 @@ export default {
</div>
</div>
<div class="popover-content">
<table>
<table class="prometheus-table">
<tr
v-for="(series, index) in timeSeries"
:key="index"
......
<script>
import { formatRelevantDigits } from '../../../lib/utils/number_utils';
import { formatRelevantDigits } from '~/lib/utils/number_utils';
export default {
props: {
graphWidth: {
type: Number,
required: true,
},
graphHeight: {
type: Number,
required: true,
},
margin: {
type: Object,
required: true,
},
measurements: {
type: Object,
required: true,
},
legendTitle: {
type: String,
required: true,
},
yAxisLabel: {
type: String,
required: true,
},
timeSeries: {
type: Array,
required: true,
},
unitOfDisplay: {
type: String,
required: true,
},
currentDataIndex: {
type: Number,
required: true,
},
showLegendGroup: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
yLabelWidth: 0,
yLabelHeight: 0,
seriesXPosition: 0,
metricUsageXPosition: 0,
};
},
computed: {
textTransform() {
const yCoordinate =
(this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 || 0;
return `translate(15, ${yCoordinate}) rotate(-90)`;
},
rectTransform() {
const yCoordinate =
(this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 +
this.yLabelWidth / 2 || 0;
return `translate(0, ${yCoordinate}) rotate(-90)`;
},
xPosition() {
return (this.graphWidth + this.measurements.axisLabelLineOffset) / 2 - this.margin.right || 0;
},
yPosition() {
return this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset || 0;
unitOfDisplay: {
type: String,
required: true,
},
},
mounted() {
this.$nextTick(() => {
const bbox = this.$refs.ylabel.getBBox();
this.metricUsageXPosition = 0;
this.seriesXPosition = 0;
if (this.$refs.legendTitleSvg != null) {
this.seriesXPosition = this.$refs.legendTitleSvg[0].getBBox().width;
}
if (this.$refs.seriesTitleSvg != null) {
this.metricUsageXPosition = this.$refs.seriesTitleSvg[0].getBBox().width;
}
this.yLabelWidth = bbox.width + 10; // Added some padding
this.yLabelHeight = bbox.height + 5;
});
},
methods: {
translateLegendGroup(index) {
return `translate(0, ${12 * index})`;
},
formatMetricUsage(series) {
const value =
series.values[this.currentDataIndex] && series.values[this.currentDataIndex].value;
series.values[this.currentDataIndex] &&
series.values[this.currentDataIndex].value;
if (isNaN(value)) {
return '-';
}
return `${formatRelevantDigits(value)} ${this.unitOfDisplay}`;
},
createSeriesString(index, series) {
if (series.metricTag) {
return `${series.metricTag} ${this.formatMetricUsage(series)}`;
}
return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`;
return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(
series,
)}`;
},
summaryMetrics(series) {
return `Avg: ${formatRelevantDigits(series.average)} ${this.unitOfDisplay},
Max: ${formatRelevantDigits(series.max)} ${this.unitOfDisplay}`;
},
strokeDashArray(type) {
if (type === 'dashed') return '6, 3';
if (type === 'dotted') return '3, 3';
......@@ -116,89 +54,38 @@ export default {
};
</script>
<template>
<g class="axis-label-container">
<line
class="label-x-axis-line"
stroke="#000000"
stroke-width="1"
x1="10"
:y1="yPosition"
:x2="graphWidth + 20"
:y2="yPosition"
/>
<line
class="label-y-axis-line"
stroke="#000000"
stroke-width="1"
x1="10"
y1="0"
:x2="10"
:y2="yPosition"
/>
<rect
class="rect-axis-text"
:transform="rectTransform"
:width="yLabelWidth"
:height="yLabelHeight"
/>
<text
class="label-axis-text y-label-text"
text-anchor="middle"
:transform="textTransform"
ref="ylabel"
>
{{ yAxisLabel }}
</text>
<rect
class="rect-axis-text"
:x="xPosition + 60"
:y="graphHeight - 80"
width="35"
height="50"
/>
<text
class="label-axis-text x-label-text"
:x="xPosition + 60"
:y="yPosition"
dy=".35em"
>
Time
</text>
<template v-if="showLegendGroup">
<g
class="legend-group"
<div class="prometheus-graph-legends prepend-left-10">
<table class="prometheus-table">
<tr
v-for="(series, index) in timeSeries"
:key="index"
:transform="translateLegendGroup(index)"
>
<line
:stroke="series.lineColor"
:stroke-width="measurements.legends.height"
:stroke-dasharray="strokeDashArray(series.lineStyle)"
:x1="measurements.legends.offsetX"
:x2="measurements.legends.offsetX + measurements.legends.width"
:y1="graphHeight - measurements.legends.offsetY"
:y2="graphHeight - measurements.legends.offsetY"
/>
<text
v-if="timeSeries.length > 1"
<td>
<svg
width="15"
height="6"
>
<line
:stroke-dasharray="strokeDashArray(series.lineStyle)"
:stroke="series.lineColor"
stroke-width="4"
:x1="0"
:x2="15"
:y1="2"
:y2="2"
/>
</svg>
</td>
<td
class="legend-metric-title"
ref="legendTitleSvg"
x="38"
:y="graphHeight - 30"
>
{{ createSeriesString(index, series) }}
</text>
<text
v-else
class="legend-metric-title"
ref="legendTitleSvg"
x="38"
:y="graphHeight - 30"
v-if="timeSeries.length > 1"
>
{{ legendTitle }} {{ formatMetricUsage(series) }}
</text>
</g>
</template>
</g>
{{ createSeriesString(index, series) }}, {{ summaryMetrics(series) }}
</td>
<td v-else>
{{ legendTitle }} {{ formatMetricUsage(series) }}, {{ summaryMetrics(series) }}
</td>
</tr>
</table>
</div>
</template>
import _ from 'underscore';
import { scaleLinear, scaleTime } from 'd3-scale';
import { line, area, curveLinear } from 'd3-shape';
import { extent, max } from 'd3-array';
import { extent, max, sum } from 'd3-array';
import { timeMinute } from 'd3-time';
const d3 = { scaleLinear, scaleTime, line, area, curveLinear, extent, max, timeMinute };
const d3 = {
scaleLinear,
scaleTime,
line,
area,
curveLinear,
extent,
max,
timeMinute,
sum,
};
const defaultColorPalette = {
blue: ['#1f78d1', '#8fbce8'],
......@@ -18,7 +28,15 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
function queryTimeSeries(
query,
graphWidth,
graphHeight,
graphHeightOffset,
xDom,
yDom,
lineStyle,
) {
let usedColors = [];
function pickColor(name) {
......@@ -42,11 +60,14 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
let metricTag = '';
let lineColor = '';
let areaColor = '';
const timeSeriesValues = timeSeries.values.map(d => d.value);
const maximumValue = d3.max(timeSeriesValues);
const accum = d3.sum(timeSeriesValues);
const timeSeriesScaleX = d3.scaleTime()
.range([0, graphWidth - 70]);
const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
const timeSeriesScaleY = d3.scaleLinear()
const timeSeriesScaleY = d3
.scaleLinear()
.range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(xDom);
......@@ -55,28 +76,35 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
const defined = d => !isNaN(d.value) && d.value != null;
const lineFunction = d3.line()
const lineFunction = d3
.line()
.defined(defined)
.curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
.x(d => timeSeriesScaleX(d.time))
.y(d => timeSeriesScaleY(d.value));
const areaFunction = d3.area()
const areaFunction = d3
.area()
.defined(defined)
.curve(d3.curveLinear)
.x(d => timeSeriesScaleX(d.time))
.y0(graphHeight - graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value));
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData = query.series != null &&
const timeSeriesMetricLabel =
timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData =
query.series != null &&
_.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
if (seriesCustomizationData) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else {
metricTag = timeSeriesMetricLabel || query.label || `series ${timeSeriesNumber + 1}`;
metricTag =
timeSeriesMetricLabel ||
query.label ||
`series ${timeSeriesNumber + 1}`;
[lineColor, areaColor] = pickColor();
}
......@@ -89,6 +117,8 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX,
values: timeSeries.values,
max: maximumValue,
average: accum / timeSeries.values.length,
lineStyle,
lineColor,
areaColor,
......@@ -97,10 +127,22 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
});
}
export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
const allValues = queries.reduce((allQueryResults, query) => allQueryResults.concat(
query.result.reduce((allResults, result) => allResults.concat(result.values), []),
), []);
export default function createTimeSeries(
queries,
graphWidth,
graphHeight,
graphHeightOffset,
) {
const allValues = queries.reduce(
(allQueryResults, query) =>
allQueryResults.concat(
query.result.reduce(
(allResults, result) => allResults.concat(result.values),
[],
),
),
[],
);
const xDom = d3.extent(allValues, d => d.time);
const yDom = [0, d3.max(allValues.map(d => d.value))];
......@@ -108,7 +150,15 @@ export default function createTimeSeries(queries, graphWidth, graphHeight, graph
return queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
return series.concat(
queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle),
queryTimeSeries(
query,
graphWidth,
graphHeight,
graphHeightOffset,
xDom,
yDom,
lineStyle,
),
);
}, []);
}
......@@ -273,21 +273,6 @@
line-height: 1.2;
}
table {
border-collapse: collapse;
padding: 0;
margin: 0;
}
td {
vertical-align: middle;
+ td {
padding-left: 5px;
vertical-align: top;
}
}
.deploy-meta-content {
border-bottom: 1px solid $white-dark;
......@@ -323,6 +308,21 @@
}
}
.prometheus-table {
border-collapse: collapse;
padding: 0;
margin: 0;
td {
vertical-align: middle;
+ td {
padding-left: 5px;
vertical-align: top;
}
}
}
.prometheus-svg-container {
position: relative;
height: 0;
......@@ -330,8 +330,7 @@
padding: 0;
padding-bottom: 100%;
.text-metric-usage,
.legend-metric-title {
.text-metric-usage {
fill: $black;
font-weight: $gl-font-weight-normal;
font-size: 12px;
......@@ -374,10 +373,6 @@
}
}
.text-metric-title {
font-size: 12px;
}
.y-label-text,
.x-label-text {
fill: $gray-darkest;
......
---
title: Add average and maximum summary statistics to the prometheus dashboard
merge_request: 17921
author:
type: changed
import Vue from 'vue';
import GraphAxis from '~/monitoring/components/graph/axis.vue';
import measurements from '~/monitoring/utils/measurements';
const createComponent = propsData => {
const Component = Vue.extend(GraphAxis);
return new Component({
propsData,
}).$mount();
};
const defaultValuesComponent = {
graphWidth: 500,
graphHeight: 300,
graphHeightOffset: 120,
margin: measurements.large.margin,
measurements: measurements.large,
yAxisLabel: 'Values',
};
function getTextFromNode(component, selector) {
return component.$el.querySelector(selector).firstChild.nodeValue.trim();
}
describe('Axis', () => {
describe('Computed props', () => {
it('textTransform', () => {
const component = createComponent(defaultValuesComponent);
expect(component.textTransform).toContain(
'translate(15, 120) rotate(-90)',
);
});
it('xPosition', () => {
const component = createComponent(defaultValuesComponent);
expect(component.xPosition).toEqual(180);
});
it('yPosition', () => {
const component = createComponent(defaultValuesComponent);
expect(component.yPosition).toEqual(240);
});
it('rectTransform', () => {
const component = createComponent(defaultValuesComponent);
expect(component.rectTransform).toContain(
'translate(0, 120) rotate(-90)',
);
});
});
it('has 2 rect-axis-text rect svg elements', () => {
const component = createComponent(defaultValuesComponent);
expect(component.$el.querySelectorAll('.rect-axis-text').length).toEqual(2);
});
it('contains text to signal the usage, title and time with multiple time series', () => {
const component = createComponent(defaultValuesComponent);
expect(getTextFromNode(component, '.y-label-text')).toEqual(
component.yAxisLabel,
);
});
});
import Vue from 'vue';
import GraphLegend from '~/monitoring/components/graph/legend.vue';
import measurements from '~/monitoring/utils/measurements';
import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(GraphLegend);
return new Component({
propsData,
}).$mount();
};
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const defaultValuesComponent = {
graphWidth: 500,
graphHeight: 300,
graphHeightOffset: 120,
margin: measurements.large.margin,
measurements: measurements.large,
areaColorRgb: '#f0f0f0',
legendTitle: 'Title',
yAxisLabel: 'Values',
metricUsage: 'Value',
unitOfDisplay: 'Req/Sec',
currentDataIndex: 0,
};
const timeSeries = createTimeSeries(convertedMetrics[0].queries,
defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight,
defaultValuesComponent.graphHeightOffset);
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import {
singleRowMetricsMultipleSeries,
convertDatesMultipleSeries,
} from '../mock_data';
defaultValuesComponent.timeSeries = timeSeries;
function getTextFromNode(component, selector) {
return component.$el.querySelector(selector).firstChild.nodeValue.trim();
}
const convertedMetrics = convertDatesMultipleSeries(
singleRowMetricsMultipleSeries,
);
describe('GraphLegend', () => {
describe('Computed props', () => {
it('textTransform', () => {
const component = createComponent(defaultValuesComponent);
const defaultValuesComponent = {};
expect(component.textTransform).toContain('translate(15, 120) rotate(-90)');
});
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
it('xPosition', () => {
const component = createComponent(defaultValuesComponent);
defaultValuesComponent.timeSeries = timeSeries;
expect(component.xPosition).toEqual(180);
});
describe('Legend Component', () => {
let vm;
let Legend;
it('yPosition', () => {
const component = createComponent(defaultValuesComponent);
beforeEach(() => {
Legend = Vue.extend(GraphLegend);
});
expect(component.yPosition).toEqual(240);
describe('Methods', () => {
beforeEach(() => {
vm = mountComponent(Legend, {
legendTitle: 'legend',
timeSeries,
currentDataIndex: 0,
unitOfDisplay: 'Req/Sec',
});
});
it('rectTransform', () => {
const component = createComponent(defaultValuesComponent);
it('formatMetricUsage should contain the unit of display and the current value selected via "currentDataIndex"', () => {
const formattedMetricUsage = vm.formatMetricUsage(timeSeries[0]);
const valueFromSeries = timeSeries[0].values[vm.currentDataIndex].value;
expect(component.rectTransform).toContain('translate(0, 120) rotate(-90)');
expect(formattedMetricUsage.indexOf(vm.unitOfDisplay)).not.toEqual(-1);
expect(formattedMetricUsage.indexOf(valueFromSeries)).not.toEqual(-1);
});
});
describe('methods', () => {
it('translateLegendGroup should only change Y direction', () => {
const component = createComponent(defaultValuesComponent);
it('strokeDashArray', () => {
const dashedArray = vm.strokeDashArray('dashed');
const dottedArray = vm.strokeDashArray('dotted');
const translatedCoordinate = component.translateLegendGroup(1);
expect(translatedCoordinate.indexOf('translate(0, ')).not.toEqual(-1);
expect(dashedArray).toEqual('6, 3');
expect(dottedArray).toEqual('3, 3');
});
it('formatMetricUsage should contain the unit of display and the current value selected via "currentDataIndex"', () => {
const component = createComponent(defaultValuesComponent);
it('summaryMetrics gets the average and max of a series', () => {
const summary = vm.summaryMetrics(timeSeries[0]);
const formattedMetricUsage = component.formatMetricUsage(timeSeries[0]);
const valueFromSeries = timeSeries[0].values[component.currentDataIndex].value;
expect(formattedMetricUsage.indexOf(component.unitOfDisplay)).not.toEqual(-1);
expect(formattedMetricUsage.indexOf(valueFromSeries)).not.toEqual(-1);
expect(summary.indexOf('Max')).not.toEqual(-1);
expect(summary.indexOf('Avg')).not.toEqual(-1);
});
});
it('has 2 rect-axis-text rect svg elements', () => {
const component = createComponent(defaultValuesComponent);
expect(component.$el.querySelectorAll('.rect-axis-text').length).toEqual(2);
});
it('contains text to signal the usage, title and time with multiple time series', () => {
const component = createComponent(defaultValuesComponent);
const titles = component.$el.querySelectorAll('.legend-metric-title');
describe('View', () => {
beforeEach(() => {
vm = mountComponent(Legend, {
legendTitle: 'legend',
timeSeries,
currentDataIndex: 0,
unitOfDisplay: 'Req/Sec',
});
});
expect(titles[0].textContent.indexOf('1xx')).not.toEqual(-1);
expect(titles[1].textContent.indexOf('2xx')).not.toEqual(-1);
expect(getTextFromNode(component, '.y-label-text')).toEqual(component.yAxisLabel);
});
it('should render the usage, title and time with multiple time series', () => {
const titles = vm.$el.querySelectorAll('.legend-metric-title');
it('should contain the same number of legend groups as the timeSeries length', () => {
const component = createComponent(defaultValuesComponent);
expect(titles[0].textContent.indexOf('1xx')).not.toEqual(-1);
expect(titles[1].textContent.indexOf('2xx')).not.toEqual(-1);
});
expect(component.$el.querySelectorAll('.legend-group').length).toEqual(component.timeSeries.length);
it('should container the same number of rows in the table as time series', () => {
expect(vm.$el.querySelectorAll('.prometheus-table tr').length).toEqual(
vm.timeSeries.length,
);
});
});
});
......@@ -2,11 +2,15 @@ import Vue from 'vue';
import Graph from '~/monitoring/components/graph.vue';
import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
import eventHub from '~/monitoring/event_hub';
import { deploymentData, convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from './mock_data';
import {
deploymentData,
convertDatesMultipleSeries,
singleRowMetricsMultipleSeries,
} from './mock_data';
const tagsPath = 'http://test.host/frontend-fixtures/environments-project/tags';
const projectPath = 'http://test.host/frontend-fixtures/environments-project';
const createComponent = (propsData) => {
const createComponent = propsData => {
const Component = Vue.extend(Graph);
return new Component({
......@@ -14,7 +18,9 @@ const createComponent = (propsData) => {
}).$mount();
};
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const convertedMetrics = convertDatesMultipleSeries(
singleRowMetricsMultipleSeries,
);
describe('Graph', () => {
beforeEach(() => {
......@@ -31,7 +37,9 @@ describe('Graph', () => {
projectPath,
});
expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(component.graphData.title);
expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(
component.graphData.title,
);
});
describe('Computed props', () => {
......@@ -46,8 +54,9 @@ describe('Graph', () => {
});
const transformedHeight = `${component.graphHeight - 100}`;
expect(component.axisTransform.indexOf(transformedHeight))
.not.toEqual(-1);
expect(component.axisTransform.indexOf(transformedHeight)).not.toEqual(
-1,
);
});
it('outerViewBox gets a width and height property based on the DOM size of the element', () => {
......@@ -63,11 +72,11 @@ describe('Graph', () => {
const viewBoxArray = component.outerViewBox.split(' ');
expect(typeof component.outerViewBox).toEqual('string');
expect(viewBoxArray[2]).toEqual(component.graphWidth.toString());
expect(viewBoxArray[3]).toEqual(component.graphHeight.toString());
expect(viewBoxArray[3]).toEqual((component.graphHeight - 50).toString());
});
});
it('sends an event to the eventhub when it has finished resizing', (done) => {
it('sends an event to the eventhub when it has finished resizing', done => {
const component = createComponent({
graphData: convertedMetrics[1],
classType: 'col-md-6',
......
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