diff --git a/app/js/controllers/grouped-runs.js b/app/js/controllers/grouped-runs.js index 9bc30671..0224de1c 100644 --- a/app/js/controllers/grouped-runs.js +++ b/app/js/controllers/grouped-runs.js @@ -114,10 +114,9 @@ function GroupedRunsController( }); }); - vm.chartData = [ - { key: 'Passes', values: passEntries, color: 'blue' }, - { key: 'Failures', values: failEntries, color: 'red' } - ]; + vm.passes = passEntries; + vm.failures = failEntries; + vm.failRates = failRateEntries; vm.chartDataRate = [ { key: '% Failures', values: failRateEntries } diff --git a/app/js/controllers/home.js b/app/js/controllers/home.js index 7c557783..3139c51d 100644 --- a/app/js/controllers/home.js +++ b/app/js/controllers/home.js @@ -50,11 +50,9 @@ function HomeController( var dateStats = projectService.getStatsByDate(projects); var entries = getChartEntries(dateStats, blanks); - vm.chartData = [ - { key: 'Passes', values: entries.passes, color: 'blue' }, - { key: 'Failures', values: entries.failures, color: 'red' } - ]; - vm.chartDataRate = [{ key: '% Failures', values: entries.failRate }]; + vm.passes = entries.passes; + vm.failures = entries.failures; + vm.failRate = entries.failRate; vm.projects = projects .sort(byFailRateDesc) .map(function(project) { return generateHorizontalBarData(project); }); diff --git a/app/js/controllers/job.js b/app/js/controllers/job.js index 3ffddd94..2975bd8e 100644 --- a/app/js/controllers/job.js +++ b/app/js/controllers/job.js @@ -140,15 +140,10 @@ function JobController( }); } - vm.chartData = [ - { key: 'Passes', values: passEntries, color: 'blue' }, - { key: 'Failures', values: failEntries, color: 'red' }, - { key: 'Skips', values: skipEntries, color: 'violet' } - ]; - - vm.chartDataRate = [ - { key: '% Failures', values: failRateEntries } - ]; + vm.passes = passEntries; + vm.failures = failEntries; + vm.skips = skipEntries; + vm.failRates = failRateEntries; vm.tests = Object.keys(tests).map(function(test) { return tests[test]; diff --git a/app/js/directives/chart-canvas-line.js b/app/js/directives/chart-canvas-line.js new file mode 100644 index 00000000..b19fc41a --- /dev/null +++ b/app/js/directives/chart-canvas-line.js @@ -0,0 +1,163 @@ +'use strict'; + +var directivesModule = require('./_index.js'); + +/** + * @ngInject + */ +function chartCanvasLine() { + var link = function(scope, el, attrs, ctrl) { + var base = ctrl.createCanvas(ctrl.width, ctrl.height, false); + var baseDirty = false; + + var overlay = ctrl.createCanvas(ctrl.width, ctrl.height, false); + var overlayDirty = false; + + var stroke = scope.stroke || 'black'; + var lineWidth = scope.lineWidth || 1; + + var dataset = null; + var screenX = null; + var screenY = null; + var dataX = null; + var dataY = null; + + var nearest = null; + + function updateAxes() { + dataset = ctrl.datasets[scope.dataset]; + if (!dataset) { + return; + } + + var axes = scope.axes.split(/[\s,]+/).map(function(name) { + return ctrl.axes[name]; + }); + screenX = axes.find(function(a) { return a.orient === 'horizontal'; }); + screenY = axes.find(function(a) { return a.orient === 'vertical'; }); + + dataX = ctrl.data(dataset.name, screenX.name); + dataY = ctrl.data(dataset.name, screenY.name); + } + + function renderBase() { + var dataset = ctrl.datasets[scope.dataset]; + if (!dataset) { + return; + } + + var ctx = base.ctx; + ctx.strokeStyle = stroke; + ctx.lineWidth = lineWidth * base.ratio; + + ctx.beginPath(); + ctx.moveTo(screenX.scale(dataX[0]), screenY.scale(dataY[0])); + for (var i = 1; i < dataX.length; i++) { + ctx.lineTo(screenX.scale(dataX[i]), screenY.scale(dataY[i])); + } + + ctx.stroke(); + + baseDirty = false; + } + + function renderOverlay() { + var ctx = overlay.ctx; + ctx.clearRect(0, 0, overlay.canvas.width, overlay.canvas.height); + + if (nearest) { + ctx.fillStyle = 'rgba(50, 50, 50, 0.15)'; + ctx.strokeStyle = 'rgba(100, 100, 100, 0.75)'; + ctx.lineWidth = 1; + + ctx.beginPath(); + ctx.arc( + screenX.scale(screenX.mapper(nearest)), + screenY.scale(screenY.mapper(nearest)), + 5 * overlay.ratio, + 0, Math.PI * 2 + ); + + ctx.fill(); + ctx.stroke(); + } + + overlayDirty = false; + } + + function handleUpdate() { + updateAxes(); + + baseDirty = true; + overlayDirty = true; + } + + scope.$on('render', function(event, canvas) { + if (baseDirty) { + renderBase(); + } + + canvas.ctx.drawImage(base.canvas, 0, 0); + }); + + scope.$on('renderOverlay', function(event, canvas) { + if (overlayDirty) { + renderOverlay(); + } + + canvas.ctx.drawImage(overlay.canvas, 0, 0); + }); + + scope.$on('update', handleUpdate); + + scope.$on('resize', function(event, width, height) { + base.resize(width, height); + overlay.resize(width, height); + }); + + scope.$on('mousemove', function(event, p) { + if (!dataset) { + return; + } + + nearest = ctrl.nearestPoint(p, dataset, screenX, screenY, 10 * base.ratio); + overlayDirty = true; + ctrl.render(); + + if (nearest) { + ctrl.tooltips.set(dataset.name, { + points: [nearest], + style: stroke + }); + } else { + ctrl.tooltips.delete(dataset.name); + } + }); + + scope.$on('mouseout', function() { + if (!dataset) { + return; + } + + nearest = null; + overlayDirty = true; + ctrl.render(); + + ctrl.tooltips.delete(dataset.name); + }); + }; + + return { + restrict: 'E', + require: '^chart', + link: link, + scope: { + dataset: '@', + axes: '@', + lineWidth: '=', + stroke: '@' + } + }; +} + +directivesModule.directive('chartCanvasLine', chartCanvasLine); diff --git a/app/js/directives/chart-line.js b/app/js/directives/chart-line.js deleted file mode 100644 index 648d65d8..00000000 --- a/app/js/directives/chart-line.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -var directivesModule = require('./_index.js'); - -var d3 = require('d3'); -var nv = require('nvd3'); - -/** - * @ngInject - */ -function chartLine() { - var link = function(scope, el, attrs) { - scope.$on('loading-started', function() { - el.css({'display' : 'none'}); - }); - - scope.$on('loading-complete', function() { - el.css({'display' : 'block'}); - }); - - var chart = null; - - var svg = d3.select(el[0]).append('svg') - .attr('width', attrs.width) - .attr('height', attrs.height); - - var update = function(data) { - if (typeof data === 'undefined') { - return; - } - - chart = nv.models.lineChart() - .margin({ left: 50, right: 50 }) - .useInteractiveGuideline(true); - - chart.tooltip.gravity('s').chartContainer(el[0]); - - chart.xAxis.tickFormat(function(d) { return d3.time.format('%m/%d %H:%M')(new Date(d)); }); - - if (attrs.forceY) { - chart.forceY(angular.fromJson(attrs.forceY)); - } - if (attrs.tickFormatX) { - chart.yAxis.tickFormat(d3.format(attrs.tickFormatX)); - } - if (attrs.tickFormatY) { - chart.yAxis.tickFormat(d3.format(attrs.tickFormatY)); - } - - svg.datum(data).call(chart); - }; - - scope.$on('windowResize', function() { - if (chart !== null) { - chart.update(); - } - }); - - scope.$watch('data', update); - }; - - return { - restrict: 'EA', - scope: { - 'data': '=', - 'width': '@', - 'height': '@' - }, - link: link - }; -} - -directivesModule.directive('chartLine', chartLine); diff --git a/app/views/grouped-runs.html b/app/views/grouped-runs.html index 26665a2a..a060fe77 100644 --- a/app/views/grouped-runs.html +++ b/app/views/grouped-runs.html @@ -31,8 +31,32 @@
- + + + + + + + + + + + +
@@ -50,8 +74,25 @@
- + + + + + + + + + +
diff --git a/app/views/home.html b/app/views/home.html index dd094986..4b0cbf76 100644 --- a/app/views/home.html +++ b/app/views/home.html @@ -35,8 +35,32 @@
- + + + + + + + + + + + +
@@ -53,8 +77,24 @@
- + + + + + + + + + +
diff --git a/app/views/job.html b/app/views/job.html index a6bdae49..27355469 100644 --- a/app/views/job.html +++ b/app/views/job.html @@ -31,8 +31,39 @@
- + + + + + + + + + + + + + +
@@ -50,8 +81,25 @@
- + + + + + + + + + +
diff --git a/test/unit/controllers/grouped_runs_spec.js b/test/unit/controllers/grouped_runs_spec.js index f0c84840..35acbc01 100644 --- a/test/unit/controllers/grouped_runs_spec.js +++ b/test/unit/controllers/grouped_runs_spec.js @@ -131,21 +131,14 @@ describe('GroupedRunsController', function() { }); $httpBackend.flush(); - var expectedChartData = [{ - key: 'Passes', - values: [{ - x: 1416355200000, y: 83 - }], - color: 'blue' - }, { - key: 'Failures', - values: [{ - x: 1416355200000, - y: 2 - }], - color: 'red' - }]; - expect(groupedRunsController.chartData).toEqual(expectedChartData); + expect(groupedRunsController.passes).toEqual([{ + x: 1416355200000, y: 83 + }]); + + expect(groupedRunsController.failures).toEqual([{ + x: 1416355200000, + y: 2 + }]); }); it('should process chart data rate correctly', function() { @@ -158,13 +151,9 @@ describe('GroupedRunsController', function() { }); $httpBackend.flush(); - var expectedChartDataRate = [{ - key: '% Failures', - values: [{ - x: 1416355200000, - y: 0.023529411764705883 - }] - }]; - expect(groupedRunsController.chartDataRate).toEqual(expectedChartDataRate); + expect(groupedRunsController.failRates).toEqual([{ + x: 1416355200000, + y: 0.023529411764705883 + }]); }); }); diff --git a/test/unit/controllers/home_spec.js b/test/unit/controllers/home_spec.js index a1658fb9..d1979dec 100644 --- a/test/unit/controllers/home_spec.js +++ b/test/unit/controllers/home_spec.js @@ -77,21 +77,12 @@ describe('HomeController', function() { }); it('should contain data for passes and failures', function() { - var expectedPasses = { - key: 'Passes', values: [{ x: timestamp, y: 3 }], color: 'blue' - }; - var expectedFailures = { - key: 'Failures', values: [{ x: timestamp, y: 4 }], color: 'red' - }; - expect(homeController.chartData).toContain(expectedPasses); - expect(homeController.chartData).toContain(expectedFailures); + expect(homeController.passes).toEqual([{ x: timestamp, y: 3 }]); + expect(homeController.failures).toEqual([{ x: timestamp, y: 4 }]); }); it('should contain data for failure rate', function() { - var expectedChartDataRate = [{ - key: '% Failures', values: [{ x: 1443729600000, y: 0.57 }] - }]; - expect(homeController.chartDataRate).toEqual(expectedChartDataRate); + expect(homeController.failRate).toEqual([{ x: 1443729600000, y: 0.57 }]); }); }); diff --git a/test/unit/controllers/job_spec.js b/test/unit/controllers/job_spec.js index 6cc71215..c94d1865 100644 --- a/test/unit/controllers/job_spec.js +++ b/test/unit/controllers/job_spec.js @@ -116,29 +116,20 @@ describe('JobController', function() { }); $httpBackend.flush(); - var expectedChartData = [{ - key: 'Passes', - values: [{ - x: 1416358800000, - y: 52 - }], - color: 'blue' - }, { - key: 'Failures', - values: [{ - x: 1416358800000, - y: 1 - }], - color: 'red' - }, { - key: 'Skips', - values: [{ - x: 1416358800000, - y: 1 - }], - color: 'violet' - }]; - expect(jobController.chartData).toEqual(expectedChartData); + expect(jobController.passes).toEqual([{ + x: 1416358800000, + y: 52 + }]); + + expect(jobController.failures).toEqual([{ + x: 1416358800000, + y: 1 + }]); + + expect(jobController.skips).toEqual([{ + x: 1416358800000, + y: 1 + }]); }); it('should process chart data rate correctly', function() { @@ -150,14 +141,10 @@ describe('JobController', function() { }); $httpBackend.flush(); - var expectedChartDataRate = [{ - key: '% Failures', - values: [{ - x: 1416358800000, - y: 0.018867924528301886 - }] - }]; - expect(jobController.chartDataRate).toEqual(expectedChartDataRate); + expect(jobController.failRates).toEqual([{ + x: 1416358800000, + y: 0.018867924528301886 + }]); }); it('should process tests correctly', function() {