summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Ivan Vargas <jvargas@gitlab.com>2018-03-21 13:36:43 -0600
committerJose Ivan Vargas <jvargas@gitlab.com>2018-03-22 15:36:15 -0600
commit1cbb90fdcbe16e46a39c61c2336914eaaef7336e (patch)
tree63e94afeacaa2bf2fe2daf2f8b6c98ebf7a76294
parenta2d82d4c5522b6d3668481e2fcfc77b0830ce9b6 (diff)
downloadgitlab-ce-jivl-summary-statistics-prometheus-dashboard.tar.gz
Add summary statistics prometheus dashboardjivl-summary-statistics-prometheus-dashboard
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue28
-rw-r--r--app/assets/javascripts/monitoring/components/graph/axis.vue127
-rw-r--r--app/assets/javascripts/monitoring/components/graph/flag.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/graph/legend.vue267
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js82
-rw-r--r--app/assets/stylesheets/pages/environments.scss37
-rw-r--r--changelogs/unreleased/jivl-summary-statistics-prometheus-dashboard.yml5
-rw-r--r--spec/javascripts/monitoring/graph/axis_spec.js70
-rw-r--r--spec/javascripts/monitoring/graph/legend_spec.js137
-rw-r--r--spec/javascripts/monitoring/graph_spec.js25
10 files changed, 446 insertions, 334 deletions
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index 42615d2bb8e..0959f45e216 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -3,6 +3,7 @@
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 @@
export default {
components: {
- GraphLegend,
+ GraphAxis,
GraphFlag,
GraphDeployment,
GraphPath,
+ GraphLegend,
},
mixins: [MonitoringMixin],
@@ -66,7 +68,7 @@
data() {
return {
- baseGraphHeight: 450,
+ baseGraphHeight: 350,
baseGraphWidth: 600,
graphHeight: 450,
graphWidth: 600,
@@ -151,7 +153,7 @@
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
@@ -192,12 +194,6 @@
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()
@@ -258,17 +254,12 @@
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"
@@ -313,5 +304,12 @@
: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>
diff --git a/app/assets/javascripts/monitoring/components/graph/axis.vue b/app/assets/javascripts/monitoring/components/graph/axis.vue
new file mode 100644
index 00000000000..ae397fc39e2
--- /dev/null
+++ b/app/assets/javascripts/monitoring/components/graph/axis.vue
@@ -0,0 +1,127 @@
+<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>
diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue
index 07aa6a3e5de..96720e2bf1b 100644
--- a/app/assets/javascripts/monitoring/components/graph/flag.vue
+++ b/app/assets/javascripts/monitoring/components/graph/flag.vue
@@ -168,7 +168,7 @@
</div>
</div>
<div class="popover-content">
- <table>
+ <table class="prometheus-table">
<tr
v-for="(series, index) in timeSeries"
:key="index"
diff --git a/app/assets/javascripts/monitoring/components/graph/legend.vue b/app/assets/javascripts/monitoring/components/graph/legend.vue
index 3149397b61f..ac1c6e34cc4 100644
--- a/app/assets/javascripts/monitoring/components/graph/legend.vue
+++ b/app/assets/javascripts/monitoring/components/graph/legend.vue
@@ -1,212 +1,91 @@
<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,
- },
+export default {
+ props: {
+ legendTitle: {
+ type: String,
+ required: true,
},
- data() {
- return {
- yLabelWidth: 0,
- yLabelHeight: 0,
- seriesXPosition: 0,
- metricUsageXPosition: 0,
- };
+ timeSeries: {
+ type: Array,
+ required: true,
},
- 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;
- },
-
+ currentDataIndex: {
+ type: Number,
+ required: true,
+ },
+ 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: {
+ formatMetricUsage(series) {
+ const value =
+ series.values[this.currentDataIndex] &&
+ series.values[this.currentDataIndex].value;
+ if (isNaN(value)) {
+ return '-';
+ }
+ return `${formatRelevantDigits(value)} ${this.unitOfDisplay}`;
},
- methods: {
- translateLegendGroup(index) {
- return `translate(0, ${12 * (index)})`;
- },
- formatMetricUsage(series) {
- const 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,
+ )}`;
+ },
- createSeriesString(index, series) {
- if (series.metricTag) {
- return `${series.metricTag} ${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';
- return null;
- },
+ strokeDashArray(type) {
+ if (type === 'dashed') return '6, 3';
+ if (type === 'dotted') return '3, 3';
+ return null;
},
- };
+ },
+};
</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>
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index b5b8e3c255d..95db31c7a2a 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -1,10 +1,20 @@
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,
+ ),
);
}, []);
}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 58700661142..9016dd3374f 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -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;
diff --git a/changelogs/unreleased/jivl-summary-statistics-prometheus-dashboard.yml b/changelogs/unreleased/jivl-summary-statistics-prometheus-dashboard.yml
new file mode 100644
index 00000000000..c5cdbcf7b40
--- /dev/null
+++ b/changelogs/unreleased/jivl-summary-statistics-prometheus-dashboard.yml
@@ -0,0 +1,5 @@
+---
+title: Add average and maximum summary statistics to the prometheus dashboard
+merge_request: 17921
+author:
+type: changed
diff --git a/spec/javascripts/monitoring/graph/axis_spec.js b/spec/javascripts/monitoring/graph/axis_spec.js
new file mode 100644
index 00000000000..a251a72cfcf
--- /dev/null
+++ b/spec/javascripts/monitoring/graph/axis_spec.js
@@ -0,0 +1,70 @@
+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,
+ );
+ });
+});
diff --git a/spec/javascripts/monitoring/graph/legend_spec.js b/spec/javascripts/monitoring/graph/legend_spec.js
index 145c8db28d5..6c085ebba89 100644
--- a/spec/javascripts/monitoring/graph/legend_spec.js
+++ b/spec/javascripts/monitoring/graph/legend_spec.js
@@ -1,106 +1,85 @@
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,
+ );
+ });
});
});
diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js
index b1d69752bad..1213c80ba3a 100644
--- a/spec/javascripts/monitoring/graph_spec.js
+++ b/spec/javascripts/monitoring/graph_spec.js
@@ -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',