Added and corrected specs for the additional time series

This commit is contained in:
Jose Ivan Vargas 2017-08-28 20:49:14 -05:00
parent 520ab72bd3
commit f6df7d6a72
10 changed files with 11803 additions and 1897 deletions

View file

@ -8,6 +8,7 @@
import eventHub from '../event_hub';
import measurements from '../utils/measurements';
import { timeScaleFormat } from '../utils/date_time_formatters';
import createTimeSeries from '../utils/multiple_time_series';
import bp from '../../breakpoints';
const bisectDate = d3.bisector(d => d.time).left;
@ -63,16 +64,10 @@
},
components: {
<<<<<<< HEAD:app/assets/javascripts/monitoring/components/graph.vue
GraphLegend,
GraphFlag,
GraphDeployment,
=======
monitoringLegends,
monitoringFlag,
monitoringDeployment,
monitoringPaths,
>>>>>>> Refactored the monitoring_column component to process all of the time series:app/assets/javascripts/monitoring/components/monitoring_column.vue
},
computed: {
@ -152,34 +147,10 @@
},
renderAxesPaths() {
this.timeSeries = this.columnData.queries[0].result.map((timeSeries) => {
const timeSeriesScaleX = d3.time.scale()
.range([0, this.graphWidth - 70]);
const timeSeriesScaleY = d3.scale.linear()
.range([this.graphHeight - this.graphHeightOffset, 0]);
timeSeriesScaleX.domain(d3.extent(timeSeries.values, d => d.time));
timeSeriesScaleY.domain([0, d3.max(timeSeries.values.map(d => d.value))]);
const lineFunction = d3.svg.line()
.x(d => timeSeriesScaleX(d.time))
.y(d => timeSeriesScaleY(d.value));
const areaFunction = d3.svg.area()
.x(d => timeSeriesScaleX(d.time))
.y0(this.graphHeight - this.graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value))
.interpolate('linear');
return {
linePath: lineFunction(timeSeries.values),
areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX,
timeSeriesScaleY,
values: timeSeries.values,
};
});
this.timeSeries = createTimeSeries(this.columnData.queries[0].result,
this.graphWidth,
this.graphHeight,
this.graphHeightOffset);
if (this.timeSeries.length > 4) {
this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 4) * 20;
@ -273,23 +244,6 @@
class="graph-data"
:viewBox="innerViewBox"
ref="graphData">
<<<<<<< HEAD:app/assets/javascripts/monitoring/components/graph.vue
<path
class="metric-area"
:d="area"
:fill="areaColorRgb"
transform="translate(-5, 20)">
</path>
<path
class="metric-line"
:d="line"
:stroke="lineColorRgb"
fill="none"
stroke-width="2"
transform="translate(-5, 20)">
</path>
<graph-deployment
=======
<monitoring-paths
v-for="(path, index) in timeSeries"
:key="index"
@ -307,7 +261,6 @@
@mousemove="handleMouseOverGraph($event)">
</rect>
<monitoring-deployment
>>>>>>> Refactored the monitoring_column component to process all of the time series:app/assets/javascripts/monitoring/components/monitoring_column.vue
:show-deploy-info="showDeployInfo"
:deployment-data="reducedDeploymentData"
:graph-height="graphHeight"

View file

@ -1,5 +1,5 @@
<script>
import { formatRelevantDigits } from '../../lib/utils/number_utils';
import { formatRelevantDigits } from '../../../lib/utils/number_utils';
export default {
props: {
@ -89,8 +89,14 @@
mounted() {
this.$nextTick(() => {
const bbox = this.$refs.ylabel.getBBox();
this.seriesXPosition = this.$refs.legendTitleSvg[0].getBBox().width;
this.metricUsageXPosition = this.$refs.seriesTitleSvg[0].getBBox().width;
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;
});

View file

@ -0,0 +1,32 @@
import d3 from 'd3';
export default function createTimeSeries(seriesData, graphWidth, graphHeight, graphHeightOffset) {
return seriesData.map((timeSeries) => {
const timeSeriesScaleX = d3.time.scale()
.range([0, graphWidth - 70]);
const timeSeriesScaleY = d3.scale.linear()
.range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(d3.extent(timeSeries.values, d => d.time));
timeSeriesScaleY.domain([0, d3.max(timeSeries.values.map(d => d.value))]);
const lineFunction = d3.svg.line()
.x(d => timeSeriesScaleX(d.time))
.y(d => timeSeriesScaleY(d.value));
const areaFunction = d3.svg.area()
.x(d => timeSeriesScaleX(d.time))
.y0(graphHeight - graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value))
.interpolate('linear');
return {
linePath: lineFunction(timeSeries.values),
areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX,
timeSeriesScaleY,
values: timeSeries.values,
};
});
}

View file

@ -32,10 +32,6 @@ describe('GraphFlag', () => {
.toEqual(component.currentXCoordinate);
expect(getCoordinate(component, '.selected-metric-line', 'x2'))
.toEqual(component.currentXCoordinate);
expect(getCoordinate(component, '.circle-metric', 'cx'))
.toEqual(component.currentXCoordinate);
expect(getCoordinate(component, '.circle-metric', 'cy'))
.toEqual(component.currentYCoordinate);
});
it('has a SVG with the class rect-text-metric at the currentFlagPosition', () => {

View file

@ -1,6 +1,8 @@
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);
@ -10,6 +12,28 @@ const createComponent = (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[0].result,
defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight,
defaultValuesComponent.graphHeightOffset);
defaultValuesComponent.timeSeries = timeSeries;
function getTextFromNode(component, selector) {
return component.$el.querySelector(selector).firstChild.nodeValue.trim();
}
@ -17,95 +41,67 @@ function getTextFromNode(component, selector) {
describe('GraphLegend', () => {
describe('Computed props', () => {
it('textTransform', () => {
const component = createComponent({
graphWidth: 500,
graphHeight: 300,
margin: measurements.large.margin,
measurements: measurements.large,
areaColorRgb: '#f0f0f0',
legendTitle: 'Title',
yAxisLabel: 'Values',
metricUsage: 'Value',
});
const component = createComponent(defaultValuesComponent);
expect(component.textTransform).toContain('translate(15, 120) rotate(-90)');
});
it('xPosition', () => {
const component = createComponent({
graphWidth: 500,
graphHeight: 300,
margin: measurements.large.margin,
measurements: measurements.large,
areaColorRgb: '#f0f0f0',
legendTitle: 'Title',
yAxisLabel: 'Values',
metricUsage: 'Value',
});
const component = createComponent(defaultValuesComponent);
expect(component.xPosition).toEqual(180);
});
it('yPosition', () => {
const component = createComponent({
graphWidth: 500,
graphHeight: 300,
margin: measurements.large.margin,
measurements: measurements.large,
areaColorRgb: '#f0f0f0',
legendTitle: 'Title',
yAxisLabel: 'Values',
metricUsage: 'Value',
});
const component = createComponent(defaultValuesComponent);
expect(component.yPosition).toEqual(240);
});
it('rectTransform', () => {
const component = createComponent({
graphWidth: 500,
graphHeight: 300,
margin: measurements.large.margin,
measurements: measurements.large,
areaColorRgb: '#f0f0f0',
legendTitle: 'Title',
yAxisLabel: 'Values',
metricUsage: 'Value',
});
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({
graphWidth: 500,
graphHeight: 300,
margin: measurements.large.margin,
measurements: measurements.large,
areaColorRgb: '#f0f0f0',
legendTitle: 'Title',
yAxisLabel: 'Values',
metricUsage: 'Value',
describe('methods', () => {
it('translateLegendGroup should only change Y direction', () => {
const component = createComponent(defaultValuesComponent);
const translatedCoordinate = component.translateLegendGroup(1);
expect(translatedCoordinate.indexOf('translate(0, ')).not.toEqual(-1);
});
it('formatMetricUsage should contain the unit of display and the current value selected via "currentDataIndex"', () => {
const component = createComponent(defaultValuesComponent);
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);
});
});
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', () => {
const component = createComponent({
graphWidth: 500,
graphHeight: 300,
margin: measurements.large.margin,
measurements: measurements.large,
areaColorRgb: '#f0f0f0',
legendTitle: 'Title',
yAxisLabel: 'Values',
metricUsage: 'Value',
});
const component = createComponent(defaultValuesComponent);
const titles = component.$el.querySelectorAll('.legend-metric-title');
expect(getTextFromNode(component, '.text-metric-title')).toEqual(component.legendTitle);
expect(getTextFromNode(component, '.text-metric-usage')).toEqual(component.metricUsage);
expect(getTextFromNode(component, '.label-axis-text')).toEqual(component.yAxisLabel);
expect(getTextFromNode(component, '.legend-metric-title')).toEqual(component.legendTitle);
expect(titles[0].textContent.indexOf('Title')).not.toEqual(-1);
expect(titles[1].textContent.indexOf('Series')).not.toEqual(-1);
expect(getTextFromNode(component, '.y-label-text')).toEqual(component.yAxisLabel);
});
it('should contain the same number of legend groups as the timeSeries length', () => {
const component = createComponent(defaultValuesComponent);
expect(component.$el.querySelectorAll('.legend-group').length).toEqual(component.timeSeries.length);
});
});

View file

@ -1,7 +1,7 @@
import Vue from 'vue';
import GraphRow from '~/monitoring/components/graph_row.vue';
import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
import { deploymentData, singleRowMetrics } from './mock_data';
import { deploymentData, convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from './mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(GraphRow);
@ -11,15 +11,15 @@ const createComponent = (propsData) => {
}).$mount();
};
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
describe('GraphRow', () => {
beforeEach(() => {
spyOn(MonitoringMixins.methods, 'formatDeployments').and.returnValue({});
});
describe('Computed props', () => {
it('bootstrapClass is set to col-md-6 when rowData is higher/equal to 2', () => {
const component = createComponent({
rowData: singleRowMetrics,
rowData: convertedMetrics,
updateAspectRatio: false,
deploymentData,
});
@ -29,7 +29,7 @@ describe('GraphRow', () => {
it('bootstrapClass is set to col-md-12 when rowData is lower than 2', () => {
const component = createComponent({
rowData: [singleRowMetrics[0]],
rowData: [convertedMetrics[0]],
updateAspectRatio: false,
deploymentData,
});
@ -40,7 +40,7 @@ describe('GraphRow', () => {
it('has one column', () => {
const component = createComponent({
rowData: singleRowMetrics,
rowData: convertedMetrics,
updateAspectRatio: false,
deploymentData,
});
@ -51,7 +51,7 @@ describe('GraphRow', () => {
it('has two columns', () => {
const component = createComponent({
rowData: singleRowMetrics,
rowData: convertedMetrics,
updateAspectRatio: false,
deploymentData,
});

View file

@ -1,9 +1,8 @@
import Vue from 'vue';
import _ from 'underscore';
import Graph from '~/monitoring/components/graph.vue';
import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
import eventHub from '~/monitoring/event_hub';
import { deploymentData, singleRowMetrics } from './mock_data';
import { deploymentData, convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from './mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(Graph);
@ -13,6 +12,8 @@ const createComponent = (propsData) => {
}).$mount();
};
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
describe('Graph', () => {
beforeEach(() => {
spyOn(MonitoringMixins.methods, 'formatDeployments').and.returnValue({});
@ -20,7 +21,7 @@ describe('Graph', () => {
it('has a title', () => {
const component = createComponent({
graphData: singleRowMetrics[0],
columnData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
@ -29,29 +30,10 @@ describe('Graph', () => {
expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(component.graphData.title);
});
it('creates a path for the line and area of the graph', (done) => {
const component = createComponent({
graphData: singleRowMetrics[0],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
});
Vue.nextTick(() => {
expect(component.area).toBeDefined();
expect(component.line).toBeDefined();
expect(typeof component.area).toEqual('string');
expect(typeof component.line).toEqual('string');
expect(_.isFunction(component.xScale)).toBe(true);
expect(_.isFunction(component.yScale)).toBe(true);
done();
});
});
describe('Computed props', () => {
it('axisTransform translates an element Y position depending of its height', () => {
const component = createComponent({
graphData: singleRowMetrics[0],
columnData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
@ -64,7 +46,7 @@ describe('Graph', () => {
it('outterViewBox gets a width and height property based on the DOM size of the element', () => {
const component = createComponent({
graphData: singleRowMetrics[0],
columnData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
@ -79,7 +61,7 @@ describe('Graph', () => {
it('sends an event to the eventhub when it has finished resizing', (done) => {
const component = createComponent({
graphData: singleRowMetrics[0],
columnData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
@ -95,7 +77,7 @@ describe('Graph', () => {
it('has a title for the y-axis and the chart legend that comes from the backend', () => {
const component = createComponent({
graphData: singleRowMetrics[0],
columnData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
import Vue from 'vue';
import MonitoringPaths from '~/monitoring/components/monitoring_paths.vue';
import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from './mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringPaths);
return new Component({
propsData,
}).$mount();
};
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0].result, 428, 272, 120);
describe('Monitoring Paths', () => {
it('renders two paths to represent a line and the area underneath it', () => {
const component = createComponent({
generatedLinePath: timeSeries[0].linePath,
generatedAreaPath: timeSeries[0].areaPath,
lineColor: '#ccc',
areaColor: '#fff',
});
const metricArea = component.$el.querySelector('.metric-area');
const metricLine = component.$el.querySelector('.metric-line');
expect(metricArea.getAttribute('fill')).toBe('#fff');
expect(metricArea.getAttribute('d')).toBe(timeSeries[0].areaPath);
expect(metricLine.getAttribute('stroke')).toBe('#ccc');
expect(metricLine.getAttribute('d')).toBe(timeSeries[0].linePath);
});
});

View file

@ -0,0 +1,20 @@
import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0].result, 428, 272, 120);
describe('Multiple time series', () => {
it('createTimeSeries returned array contains an object for each element', () => {
expect(typeof timeSeries[0].linePath).toEqual('string');
expect(typeof timeSeries[0].areaPath).toEqual('string');
expect(typeof timeSeries[0].timeSeriesScaleX).toEqual('function');
expect(typeof timeSeries[0].timeSeriesScaleY).toEqual('function');
expect(timeSeries[0].values instanceof Array).toEqual(true);
});
it('createTimeSeries returns an array', () => {
expect(timeSeries instanceof Array).toEqual(true);
expect(timeSeries.length).toEqual(5);
});
});