Browse Source

feat(plugins): work on plugin directive loading

Torkel Ödegaard 10 years ago
parent
commit
dbafc8c9db

+ 1 - 0
public/app/features/panel/all.js

@@ -4,4 +4,5 @@ define([
   './panel_srv',
   './panel_helper',
   './solo_panel_ctrl',
+  './panel_loader',
 ], function () {});

+ 1 - 21
public/app/features/panel/panel_directive.js

@@ -1,32 +1,12 @@
 define([
   'angular',
   'jquery',
-  'app/core/config',
 ],
-function (angular, $, config) {
+function (angular, $) {
   'use strict';
 
   var module = angular.module('grafana.directives');
 
-  module.directive('panelLoader', function($compile, $parse) {
-    return {
-      restrict: 'E',
-      link: function(scope, elem, attr) {
-        var getter = $parse(attr.type), panelType = getter(scope);
-        var module = config.panels[panelType].module;
-
-        System.import(module).then(function() {
-          var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
-          elem.append(panelEl);
-          $compile(panelEl)(scope);
-        }).catch(function(err) {
-          console.log('Failed to load panel:', err);
-          scope.appEvent('alert-error', ['Panel Load Error', 'Failed to load panel ' + panelType + ', ' + err]);
-        });
-      }
-    };
-  });
-
   module.directive('grafanaPanel', function() {
     return {
       restrict: 'E',

+ 22 - 0
public/app/features/panel/panel_loader.ts

@@ -0,0 +1,22 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import config from 'app/core/config';
+
+/** @ngInject */
+function panelLoader($parse, dynamicDirectiveSrv) {
+  return dynamicDirectiveSrv.create({
+    directive: scope => {
+      let modulePath = config.panels[scope.panel.type].module;
+
+      return System.import(modulePath).then(function(panelModule) {
+        return {
+          name: 'panel-directive-' + scope.panel.type,
+          fn: panelModule.panel,
+        };
+      });
+    },
+  });
+}
+
+angular.module('grafana.directives').directive('panelLoader', panelLoader);

+ 0 - 220
public/app/panels/graph/alerting.html

@@ -1,220 +0,0 @@
-<div class="editor-row" style="margin-bottom: 20px;">
-  <span style="float: right; font-size: 12px;"><i>Last updated by Grafana October 4, 2015 12:15:04 by $username</i></span>
-  <div class="section">
-    <h5>General Alerting Options</h5>
-    <div class="tight-form last">
-      <ul class="tight-form-list">
-        <li class="tight-form-item">
-          Alert Title
-        </li>
-        <li>
-          <input type="text" class="input-xlarge tight-form-input"></input>
-        </li>
-        <li class="tight-form-item">
-          Alerting Backend
-        </li>
-        <li>
-          <select class="input-medium tight-form-input">
-            <option>Grafana Alerting</option>
-          </select>
-        </li>
-        <li class="tight-form-item last">
-          <label class="checkbox-label" for="alerting-enabled">Enabled</label>
-          <input class="cr1" id="alerting-enabled" type="checkbox">
-          <label for="alerting-enabled" class="cr1"></label>
-        </li>
-      </ul>
-      <div class="clearfix"></div>
-    </div>
-  </div>
-</div>
-<div class="editor-row" style="margin-bottom: 20px;">
-  <h5>Choose your query:</h5>
-  <p>Select an exising query to alert on:</p>
-  <div class="tight-form">
-    <ul class="tight-form-list">
-      <li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
-      <li class="tight-form-item">None</li>
-    </ul>
-    <div class="clearfix"></div>
-  </div>
-  <div class="tight-form">
-    <ul class="tight-form-list">
-      <li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
-      <li class="tight-form-item" style="min-width: 15px; text-align: center">A</li>
-      <li class="tight-form-item">apps</li>
-      <li class="tight-form-item"><i class="fa fa-asterisk"><i></i></i></li>
-      <li class="tight-form-item">fakesite</li>
-      <li class="tight-form-item">counters</li>
-      <li class="tight-form-item">requests</li>
-      <li class="tight-form-item">count</li>
-      <li class="tight-form-item">scaleToSeconds(1)</li>
-      <li class="tight-form-item last">aliasByNode(2)</li>
-      <li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
-    </ul>
-    <div class="clearfix"></div>
-  </div>
-  <div class="tight-form">
-    <ul class="tight-form-list">
-      <li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
-      <li class="tight-form-item" style="min-width: 15px; text-align: center">B</li>
-      <li class="tight-form-item last"><span class="query-keyword">Metric:</span> us-west-2 AWS/EC2 CPUUtilization <span class="query-keyword">Stats:</span> Minimum Maximum <span class="query-keyword">Dimensions</span> InstanceIS <span class="query-segment-operator">=</span> i-b0e8a447 <span class="query-keyword">Alias</span> {{stat}} <span class="query-keyword">Period</span> 60</li>
-      <li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
-    </ul>
-    <div class="clearfix"></div>
-  </div>
-  <div class="tight-form">
-    <ul class="tight-form-list">
-      <li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
-      <li class="tight-form-item" style="min-width: 15px; text-align: center">C</li>
-      <li class="tight-form-item last"><span class="query-keyword">Query:</span> avg(counters_logins) by(server) <span class="query-keyword">Legend Format:</span> {{app}} - {{server}} <span class="query-keyword">Step:</span> 1s <span class="query-keyword">Resolution:</span> 1/2</li>
-      <li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
-    </ul>
-    <div class="clearfix"></div>
-  </div>
-  <div class="tight-form">
-    <ul class="tight-form-list">
-      <li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
-      <li class="tight-form-item" style="min-width: 15px; text-align: center">D</li>
-      <li class="tight-form-item last"><span class="query-keyword">SELECT</span> mean(value) <span class="query-keyword">FROM</span> logins.count <span class="query-keyword">WHERE</span> hostname <span class="query-segment-operator">=</span> /$Hostname$/ <span class="query-keyword">GROUP BY</span> time($internal) hostname</li>
-      <li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
-    </ul>
-    <div class="clearfix"></div>
-  </div>
-  <div class="tight-form last">
-    <ul class="tight-form-list">
-      <li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" checked /></li>
-      <li class="tight-form-item" style="min-width: 15px; text-align: center">E</li>
-      <li class="tight-form-item last"><span class="query-keyword">Metric:</span> apps.backend.backend_01.counters.requests.count <span class="query-keyword">Alias:</span> Bristow <span class="query-keyword">Aggregator:</span> Sum <span class="query-keyword">Downsample:</span> 1m <span class="query-keyword">Aggregator</span> Sum <span class="query-keyword">Tags</span> host = test</li>
-      <li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
-    </ul>
-    <div class="clearfix"></div>
-  </div>
-</div>
-<div class="editor-row" style="margin-bottom: 20px;">
-  <p>Or write a new custom alerting query:</p>
-  <div class="section">
-    <div class="tight-form last">
-      <ul class="tight-form-list">
-        <li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
-        <li class="tight-form-item">
-          <a class="pointer">
-            <i class="fa fa-pencil"></i>
-          </a>
-        </li>
-        <li class="tight-form-item">
-          select metric
-        </li>
-        <li>
-          <a class="tight-form-item tight-form-func last dropdown-toggle"><i class="fa fa-plus"></i></a>
-        </li>
-      </ul>
-      <div class="clearfix"></div>
-    </div>
-  </div>
-</div>
-<div class="editor-row" style="margin-bottom: 10px;">
-  <div class="section">
-    <h5>Define Your States</h5>
-    <div class="tight-form last">
-      <ul class="tight-form-list">
-        <li class="tight-form-item">
-          by
-        </li>
-        <li>
-          <select class="input-medium tight-form-input">
-            <option>Averaging</option>
-          </select>
-        </li>
-        <li class="tight-form-item">
-          the values in the query over the last
-        </li>
-        <li>
-          <input type="text" class="input-mini tight-form-input last"></input>
-        </li>
-      </ul>
-      <div class="clearfix"></div>
-    </div>
-  </div>
-</div>
-<div class="editor-row" style="margin-bottom: 20px;">
-  <div class="section">
-    <div class="tight-form">
-      <ul class="tight-form-list">
-        <li class="tight-form-item" style="width: 100px;">
-          <span class="alert-state alert-state-warning">Warn</span>
-        </li>
-        <li>
-          <input type="text" class="input-mini tight-form-input" value=">" style="text-align: center;"></input>
-        </li>
-        <li>
-          <input type="text" class="input-mini tight-form-input" value="#B" style="text-align: center;"></input>
-        </li>
-        <li class="tight-form-item">
-          .notify
-        </li>
-        <li class="alert-notify-emails">
-          <bootstrap-tagsinput tagclass="label label-tag label-tag-email"></bootstrap-tagsinput>
-        </li>
-        <li class="tight-form-item last">
-          <label class="checkbox-label" for="state-enabled">Enabled</label>
-          <input class="cr1" id="state-enabled" type="checkbox">
-          <label for="state-enabled" class="cr1"></label>
-        </li>
-      </ul>
-      <div class="clearfix"></div>
-    </div>
-    <div class="tight-form last">
-      <ul class="tight-form-list">
-        <li class="tight-form-item" style="width: 100px;">
-          <span class="alert-state alert-state-critical">Critical</span>
-        </li>
-        <li>
-          <input type="text" class="input-mini tight-form-input"></input>
-        </li>
-        <li>
-          <input type="text" class="input-mini tight-form-input"></input>
-        </li>
-        <li class="tight-form-item">
-          .notify
-        </li>
-        <li class="alert-notify-emails">
-          <bootstrap-tagsinput tagclass="label label-tag label-tag-email"></bootstrap-tagsinput>
-        </li>
-        <li class="tight-form-item last">
-          <label class="checkbox-label" for="state-enabled2">Enabled</label>
-          <input class="cr1" id="state-enabled2" type="checkbox">
-          <label for="state-enabled2" class="cr1"></label>
-        </li>
-      </ul>
-      <div class="clearfix"></div>
-    </div>
-  </div>
-</div>
-<div class="editor-row">
-  <div class="section">
-    <h5>What to Say <span style="float: right; font-size: 12px; font-weight: normal;"><a href="#">Variables</a> | <a href="#">Preview</a></span></h5>
-    <div class="tight-form">
-      <ul class="tight-form-list">
-        <li class="tight-form-item" style="width: 100px;">
-          Summary
-        </li>
-        <li>
-          <input type="text" class="input-xxlarge tight-form-input last"></input>
-        </li>
-      </ul>
-      <div class="clearfix"></div>
-    </div>
-    <div class="tight-form last">
-      <ul class="tight-form-list">
-        <li class="tight-form-item" style="width: 100px;">
-          Description
-        </li>
-        <li>
-          <textarea class="tight-form-textarea input-xxlarge last"></textarea>
-        </li>
-      </ul>
-      <div class="clearfix"></div>
-    </div>
-  </div>
-</div>

+ 1 - 1
public/app/partials/dashboard.html

@@ -83,7 +83,7 @@
 
 					<div ng-repeat="(name, panel) in row.panels track by panel.id" class="panel" ui-draggable="!dashboardViewState.fullscreen" drag="panel.id"
 						ui-on-Drop="onDrop($data, row, panel)" drag-handle-class="drag-handle" panel-width>
-						<panel-loader type="panel.type" class="panel-margin"></panel-loader>
+						<panel-loader class="panel-margin"></panel-loader>
 					</div>
 
 					<div panel-drop-zone class="panel panel-drop-zone" ui-on-drop="onDrop($data, row)" data-drop="true">

+ 14 - 9
public/app/plugins/panel/dashlist/module.js

@@ -11,14 +11,8 @@ function (angular, app, _, config, PanelMeta) {
   var module = angular.module('grafana.panels.dashlist', []);
   app.useModule(module);
 
-  module.directive('grafanaPanelDashlist', function() {
-    return {
-      controller: 'DashListPanelCtrl',
-      templateUrl: 'app/plugins/panel/dashlist/module.html',
-    };
-  });
-
-  module.controller('DashListPanelCtrl', function($scope, panelSrv, backendSrv) {
+  /** @ngInject */
+  function DashListPanelCtrl($scope, panelSrv, backendSrv) {
 
     $scope.panelMeta = new PanelMeta({
       panelName: 'Dashboard list',
@@ -73,5 +67,16 @@ function (angular, app, _, config, PanelMeta) {
     };
 
     $scope.init();
-  });
+  }
+
+  function dashListPanelDirective() {
+    return {
+      controller: DashListPanelCtrl,
+      templateUrl: 'app/plugins/panel/dashlist/module.html',
+    };
+  }
+
+  return {
+    panel: dashListPanelDirective
+  };
 });

+ 1 - 1
public/app/plugins/panel/graph/graph.js

@@ -4,7 +4,7 @@ define([
   'moment',
   'lodash',
   'app/core/utils/kbn',
-  './graph.tooltip',
+  './graph_tooltip',
   'jquery.flot',
   'jquery.flot.events',
   'jquery.flot.selection',

+ 2 - 0
public/app/plugins/panel/graph/graph_tooltip.d.ts

@@ -0,0 +1,2 @@
+declare var GraphTooltip: any;
+export default GraphTooltip;

+ 0 - 0
public/app/plugins/panel/graph/graph.tooltip.js → public/app/plugins/panel/graph/graph_tooltip.js


+ 3 - 0
public/app/plugins/panel/graph/module.d.ts

@@ -0,0 +1,3 @@
+declare var panel: any;
+declare var GraphCtrl: any;
+export {panel, GraphCtrl};

+ 13 - 11
public/app/plugins/panel/graph/module.js

@@ -12,16 +12,8 @@ define([
 function (angular, _, moment, kbn, TimeSeries, PanelMeta) {
   'use strict';
 
-  var module = angular.module('grafana.panels.graph');
-
-  module.directive('grafanaPanelGraph', function() {
-    return {
-      controller: 'GraphCtrl',
-      templateUrl: 'app/plugins/panel/graph/module.html',
-    };
-  });
-
-  module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, panelHelper) {
+  /** @ngInject */
+  function GraphCtrl($scope, $rootScope, panelSrv, annotationsSrv, panelHelper) {
 
     $scope.panelMeta = new PanelMeta({
       panelName: 'Graph',
@@ -294,7 +286,17 @@ function (angular, _, moment, kbn, TimeSeries, PanelMeta) {
     };
 
     panelSrv.init($scope);
+  }
 
-  });
+  function graphPanelDirective() {
+    return {
+      controller: GraphCtrl,
+      templateUrl: 'app/plugins/panel/graph/module.html',
+    };
+  }
 
+  return {
+    GraphCtrl: GraphCtrl,
+    panel: graphPanelDirective,
+  };
 });

+ 53 - 0
public/app/plugins/panel/graph/specs/graph_ctrl_specs.ts

@@ -0,0 +1,53 @@
+///<reference path="../../../../headers/common.d.ts" />
+
+import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
+
+import 'app/features/panel/panel_srv';
+import 'app/features/panel/panel_helper';
+
+import angular from 'angular';
+import {GraphCtrl} from '../module';
+import helpers from '../../../../../test/specs/helpers';
+
+angular.module('grafana.controllers').controller('GraphCtrl', GraphCtrl);
+
+describe('GraphCtrl', function() {
+  var ctx = new helpers.ControllerTestContext();
+
+  beforeEach(angularMocks.module('grafana.services'));
+  beforeEach(angularMocks.module('grafana.controllers'));
+
+  beforeEach(ctx.providePhase());
+  beforeEach(ctx.createControllerPhase('GraphCtrl'));
+
+  describe('get_data with 2 series', function() {
+    beforeEach(function() {
+      ctx.annotationsSrv.getAnnotations = sinon.stub().returns(ctx.$q.when([]));
+      ctx.datasource.query = sinon.stub().returns(ctx.$q.when({
+        data: [
+          { target: 'test.cpu1', datapoints: [[1, 10]]},
+          { target: 'test.cpu2', datapoints: [[1, 10]]}
+        ]
+      }));
+      ctx.scope.render = sinon.spy();
+      ctx.scope.refreshData(ctx.datasource);
+      ctx.scope.$digest();
+    });
+
+    it('should send time series to render', function() {
+      var data = ctx.scope.render.getCall(0).args[0];
+      expect(data.length).to.be(2);
+    });
+
+    describe('get_data failure following success', function() {
+      beforeEach(function() {
+        ctx.datasource.query = sinon.stub().returns(ctx.$q.reject('Datasource Error'));
+        ctx.scope.refreshData(ctx.datasource);
+        ctx.scope.$digest();
+      });
+
+    });
+
+  });
+
+});

+ 230 - 0
public/app/plugins/panel/graph/specs/graph_specs.ts

@@ -0,0 +1,230 @@
+///<reference path="../../../../headers/common.d.ts" />
+
+import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
+
+import '../module';
+import angular from 'angular';
+import $ from 'jquery';
+import helpers from '../../../../../test/specs/helpers';
+import TimeSeries from '../../../../core/time_series2';
+
+describe('grafanaGraph', function() {
+
+  beforeEach(angularMocks.module('grafana.directives'));
+
+  function graphScenario(desc, func)  {
+    describe(desc, function() {
+      var ctx: any = {};
+
+      ctx.setup = function(setupFunc) {
+
+        beforeEach(angularMocks.module(function($provide) {
+          $provide.value("timeSrv", new helpers.TimeSrvStub());
+        }));
+
+        beforeEach(angularMocks.inject(function($rootScope, $compile) {
+          var scope = $rootScope.$new();
+          var element = angular.element("<div style='width:500px' grafana-graph><div>");
+
+          scope.height = '200px';
+          scope.panel = {
+            legend: {},
+            grid: { },
+            y_formats: [],
+            seriesOverrides: [],
+            tooltip: {
+              shared: true
+            }
+          };
+
+          scope.panelRenderingComplete = sinon.spy();
+          scope.appEvent = sinon.spy();
+          scope.onAppEvent = sinon.spy();
+          scope.hiddenSeries = {};
+          scope.dashboard = { timezone: 'browser' };
+          scope.range = {
+            from: new Date('2014-08-09 10:00:00'),
+            to: new Date('2014-09-09 13:00:00')
+          };
+          ctx.data = [];
+          ctx.data.push(new TimeSeries({
+            datapoints: [[1,1],[2,2]],
+            alias: 'series1'
+          }));
+          ctx.data.push(new TimeSeries({
+            datapoints: [[1,1],[2,2]],
+            alias: 'series2'
+          }));
+
+          setupFunc(scope, ctx.data);
+
+          $compile(element)(scope);
+          scope.$digest();
+          $.plot = ctx.plotSpy = sinon.spy();
+
+          scope.$emit('render', ctx.data);
+          ctx.plotData = ctx.plotSpy.getCall(0).args[1];
+          ctx.plotOptions = ctx.plotSpy.getCall(0).args[2];
+        }));
+      };
+
+      func(ctx);
+    });
+  }
+
+  graphScenario('simple lines options', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.panel.lines = true;
+      scope.panel.fill = 5;
+      scope.panel.linewidth = 3;
+      scope.panel.steppedLine = true;
+    });
+
+    it('should configure plot with correct options', function() {
+      expect(ctx.plotOptions.series.lines.show).to.be(true);
+      expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
+      expect(ctx.plotOptions.series.lines.lineWidth).to.be(3);
+      expect(ctx.plotOptions.series.lines.steps).to.be(true);
+    });
+  });
+
+  graphScenario('grid thresholds 100, 200', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.panel.grid = {
+        threshold1: 100,
+        threshold1Color: "#111",
+        threshold2: 200,
+        threshold2Color: "#222",
+      };
+    });
+
+    it('should add grid markings', function() {
+      var markings = ctx.plotOptions.grid.markings;
+      expect(markings[0].yaxis.from).to.be(100);
+      expect(markings[0].yaxis.to).to.be(200);
+      expect(markings[0].color).to.be('#111');
+      expect(markings[1].yaxis.from).to.be(200);
+      expect(markings[1].yaxis.to).to.be(Infinity);
+    });
+  });
+
+  graphScenario('inverted grid thresholds 200, 100', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.panel.grid = {
+        threshold1: 200,
+        threshold1Color: "#111",
+        threshold2: 100,
+        threshold2Color: "#222",
+      };
+    });
+
+    it('should add grid markings', function() {
+      var markings = ctx.plotOptions.grid.markings;
+      expect(markings[0].yaxis.from).to.be(200);
+      expect(markings[0].yaxis.to).to.be(100);
+      expect(markings[0].color).to.be('#111');
+      expect(markings[1].yaxis.from).to.be(100);
+      expect(markings[1].yaxis.to).to.be(-Infinity);
+    });
+  });
+
+  graphScenario('grid thresholds from zero', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.panel.grid = {
+        threshold1: 0,
+        threshold1Color: "#111",
+      };
+    });
+
+    it('should add grid markings', function() {
+      var markings = ctx.plotOptions.grid.markings;
+      expect(markings[0].yaxis.from).to.be(0);
+    });
+  });
+
+  graphScenario('when logBase is log 10', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.panel.grid = {
+        leftMax: null,
+        rightMax: null,
+        leftMin: null,
+        rightMin: null,
+        leftLogBase: 10,
+      };
+    });
+
+    it('should apply axis transform and ticks', function() {
+      var axis = ctx.plotOptions.yaxes[0];
+      expect(axis.transform(100)).to.be(Math.log(100+0.1));
+      expect(axis.ticks[0]).to.be(0);
+      expect(axis.ticks[1]).to.be(1);
+    });
+  });
+
+  graphScenario('should use timeStep for barWidth', function(ctx) {
+    ctx.setup(function(scope, data) {
+      scope.panel.bars = true;
+      data[0] = new TimeSeries({
+        datapoints: [[1,10],[2,20]],
+        alias: 'series1',
+      });
+    });
+
+    it('should set barWidth', function() {
+      expect(ctx.plotOptions.series.bars.barWidth).to.be(10/1.5);
+    });
+  });
+
+  graphScenario('series option overrides, fill & points', function(ctx) {
+    ctx.setup(function(scope, data) {
+      scope.panel.lines = true;
+      scope.panel.fill = 5;
+      scope.panel.seriesOverrides = [
+        { alias: 'test', fill: 0, points: true }
+      ];
+
+      data[1].alias = 'test';
+    });
+
+    it('should match second series and fill zero, and enable points', function() {
+      expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
+      expect(ctx.plotData[1].lines.fill).to.be(0.001);
+      expect(ctx.plotData[1].points.show).to.be(true);
+    });
+  });
+
+  graphScenario('should order series order according to zindex', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.panel.seriesOverrides = [{ alias: 'series1', zindex: 2 }];
+    });
+
+    it('should move zindex 2 last', function() {
+      expect(ctx.plotData[0].alias).to.be('series2');
+      expect(ctx.plotData[1].alias).to.be('series1');
+    });
+  });
+
+  graphScenario('when series is hidden', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.hiddenSeries = {'series2': true};
+    });
+
+    it('should remove datapoints and disable stack', function() {
+      expect(ctx.plotData[0].alias).to.be('series1');
+      expect(ctx.plotData[1].data.length).to.be(0);
+      expect(ctx.plotData[1].stack).to.be(false);
+    });
+  });
+
+  graphScenario('when stack and percent', function(ctx) {
+    ctx.setup(function(scope) {
+      scope.panel.percentage = true;
+      scope.panel.stack = true;
+    });
+
+    it('should show percentage', function() {
+      var axis = ctx.plotOptions.yaxes[0];
+      expect(axis.tickFormatter(100, axis)).to.be("100%");
+    });
+  });
+});

+ 171 - 0
public/app/plugins/panel/graph/specs/tooltip_specs.ts

@@ -0,0 +1,171 @@
+///<reference path="../../../../headers/common.d.ts" />
+
+import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
+
+import $ from 'jquery';
+import GraphTooltip from '../graph_tooltip';
+
+var scope =  {
+  appEvent: sinon.spy(),
+  onAppEvent: sinon.spy(),
+};
+
+var elem = $('<div></div>');
+var dashboard = { };
+
+function describeSharedTooltip(desc, fn) {
+  var ctx: any = {};
+  ctx.scope = scope;
+  ctx.scope.panel =  {
+    tooltip:  {
+      shared: true
+    },
+    legend: { },
+    stack: false
+  };
+
+  ctx.setup = function(setupFn) {
+    ctx.setupFn = setupFn;
+  };
+
+  describe(desc, function() {
+    beforeEach(function() {
+      ctx.setupFn();
+      var tooltip = new GraphTooltip(elem, dashboard, scope);
+      ctx.results = tooltip.getMultiSeriesPlotHoverInfo(ctx.data, ctx.pos);
+    });
+
+    fn(ctx);
+  });
+}
+
+describeSharedTooltip("steppedLine false, stack false", function(ctx) {
+  ctx.setup(function() {
+    ctx.data = [
+      { data: [[10, 15], [12, 20]], lines: {} },
+      { data: [[10, 2], [12, 3]], lines: {} }
+    ];
+    ctx.pos = { x: 11 };
+  });
+
+  it('should return 2 series', function() {
+    expect(ctx.results.length).to.be(2);
+  });
+  it('should add time to results array', function() {
+    expect(ctx.results.time).to.be(10);
+  });
+  it('should set value and hoverIndex', function() {
+    expect(ctx.results[0].value).to.be(15);
+    expect(ctx.results[1].value).to.be(2);
+    expect(ctx.results[0].hoverIndex).to.be(0);
+  });
+});
+
+describeSharedTooltip("one series is hidden", function(ctx) {
+  ctx.setup(function() {
+    ctx.data = [
+      { data: [[10, 15], [12, 20]], },
+      { data: [] }
+    ];
+    ctx.pos = { x: 11 };
+  });
+});
+
+describeSharedTooltip("steppedLine false, stack true, individual false", function(ctx) {
+  ctx.setup(function() {
+    ctx.data = [
+      {
+        data: [[10, 15], [12, 20]],
+        lines: {},
+        datapoints: {
+          pointsize: 2,
+          points: [[10,15], [12,20]],
+        },
+        stack: true,
+      },
+      {
+        data: [[10, 2], [12, 3]],
+        lines: {},
+        datapoints: {
+          pointsize: 2,
+          points: [[10, 2], [12, 3]],
+        },
+        stack: true
+      }
+    ];
+    ctx.scope.panel.stack = true;
+    ctx.pos = { x: 11 };
+  });
+
+  it('should show stacked value', function() {
+    expect(ctx.results[1].value).to.be(17);
+  });
+});
+
+describeSharedTooltip("steppedLine false, stack true, individual false, series stack false", function(ctx) {
+  ctx.setup(function() {
+    ctx.data = [
+      {
+        data: [[10, 15], [12, 20]],
+        lines: {},
+        datapoints: {
+          pointsize: 2,
+          points: [[10, 15], [12, 20]],
+        },
+        stack: true
+      },
+      {
+        data: [[10, 2], [12, 3]],
+        lines: {},
+        datapoints: {
+          pointsize: 2,
+          points: [[10, 2], [12, 3]],
+        },
+        stack: false
+      }
+    ];
+    ctx.scope.panel.stack = true;
+    ctx.pos = { x: 11 };
+  });
+
+  it('should not show stacked value', function() {
+    expect(ctx.results[1].value).to.be(2);
+  });
+
+});
+
+describeSharedTooltip("steppedLine false, stack true, individual true", function(ctx) {
+  ctx.setup(function() {
+    ctx.data = [
+      {
+        data: [[10, 15], [12, 20]],
+        lines: {},
+        datapoints: {
+          pointsize: 2,
+          points: [[10, 15], [12, 20]],
+        },
+        stack: true
+      },
+      {
+        data: [[10, 2], [12, 3]],
+        lines: {},
+        datapoints: {
+          pointsize: 2,
+          points: [[10, 2], [12, 3]],
+        },
+        stack: false
+      }
+    ];
+    ctx.scope.panel.stack = true;
+    ctx.scope.panel.tooltip.value_type = 'individual';
+    ctx.pos = { x: 11 };
+  });
+
+  it('should not show stacked value', function() {
+    expect(ctx.results[1].value).to.be(2);
+  });
+
+});
+
+
+

+ 0 - 51
public/test/specs/graph-ctrl-specs.js

@@ -1,51 +0,0 @@
-define([
-  './helpers',
-  'app/features/panel/panel_srv',
-  'app/features/panel/panel_helper',
-  'app/plugins/panel/graph/module'
-], function(helpers) {
-  'use strict';
-
-  describe('GraphCtrl', function() {
-    var ctx = new helpers.ControllerTestContext();
-
-    beforeEach(module('grafana.services'));
-    beforeEach(module('grafana.panels.graph'));
-
-    beforeEach(ctx.providePhase());
-    beforeEach(ctx.createControllerPhase('GraphCtrl'));
-
-    describe('get_data with 2 series', function() {
-      beforeEach(function() {
-        ctx.annotationsSrv.getAnnotations = sinon.stub().returns(ctx.$q.when([]));
-        ctx.datasource.query = sinon.stub().returns(ctx.$q.when({
-          data: [
-            { target: 'test.cpu1', datapoints: [[1, 10]]},
-            { target: 'test.cpu2', datapoints: [[1, 10]]}
-          ]
-        }));
-        ctx.scope.render = sinon.spy();
-        ctx.scope.refreshData(ctx.datasource);
-        ctx.scope.$digest();
-      });
-
-      it('should send time series to render', function() {
-        var data = ctx.scope.render.getCall(0).args[0];
-        expect(data.length).to.be(2);
-      });
-
-      describe('get_data failure following success', function() {
-        beforeEach(function() {
-          ctx.datasource.query = sinon.stub().returns(ctx.$q.reject('Datasource Error'));
-          ctx.scope.refreshData(ctx.datasource);
-          ctx.scope.$digest();
-        });
-
-      });
-
-    });
-
-  });
-
-});
-

+ 0 - 231
public/test/specs/graph-specs.js

@@ -1,231 +0,0 @@
-define([
-  './helpers',
-  'angular',
-  'jquery',
-  'app/core/time_series',
-  'app/plugins/panel/graph/graph'
-], function(helpers, angular, $, TimeSeries) {
-  'use strict';
-
-  describe('grafanaGraph', function() {
-
-    beforeEach(module('grafana.directives'));
-
-    function graphScenario(desc, func)  {
-      describe(desc, function() {
-        var ctx = {};
-
-        ctx.setup = function (setupFunc) {
-
-          beforeEach(module(function($provide) {
-            $provide.value("timeSrv", new helpers.TimeSrvStub());
-          }));
-
-          beforeEach(inject(function($rootScope, $compile) {
-            var scope = $rootScope.$new();
-            var element = angular.element("<div style='width:500px' grafana-graph><div>");
-
-            scope.height = '200px';
-            scope.panel = {
-              legend: {},
-              grid: { },
-              y_formats: [],
-              seriesOverrides: [],
-              tooltip: {
-                shared: true
-              }
-            };
-
-            scope.panelRenderingComplete = sinon.spy();
-            scope.appEvent = sinon.spy();
-            scope.onAppEvent = sinon.spy();
-            scope.hiddenSeries = {};
-            scope.dashboard = { timezone: 'browser' };
-            scope.range = {
-              from: new Date('2014-08-09 10:00:00'),
-              to: new Date('2014-09-09 13:00:00')
-            };
-            ctx.data = [];
-            ctx.data.push(new TimeSeries({
-              datapoints: [[1,1],[2,2]],
-              alias: 'series1'
-            }));
-            ctx.data.push(new TimeSeries({
-              datapoints: [[1,1],[2,2]],
-              alias: 'series2'
-            }));
-
-            setupFunc(scope, ctx.data);
-
-            $compile(element)(scope);
-            scope.$digest();
-            $.plot = ctx.plotSpy = sinon.spy();
-
-            scope.$emit('render', ctx.data);
-            ctx.plotData = ctx.plotSpy.getCall(0).args[1];
-            ctx.plotOptions = ctx.plotSpy.getCall(0).args[2];
-          }));
-        };
-
-        func(ctx);
-      });
-    }
-
-    graphScenario('simple lines options', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.panel.lines = true;
-        scope.panel.fill = 5;
-        scope.panel.linewidth = 3;
-        scope.panel.steppedLine = true;
-      });
-
-      it('should configure plot with correct options', function() {
-        expect(ctx.plotOptions.series.lines.show).to.be(true);
-        expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
-        expect(ctx.plotOptions.series.lines.lineWidth).to.be(3);
-        expect(ctx.plotOptions.series.lines.steps).to.be(true);
-      });
-    });
-
-    graphScenario('grid thresholds 100, 200', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.panel.grid = {
-          threshold1: 100,
-          threshold1Color: "#111",
-          threshold2: 200,
-          threshold2Color: "#222",
-        };
-      });
-
-      it('should add grid markings', function() {
-        var markings = ctx.plotOptions.grid.markings;
-        expect(markings[0].yaxis.from).to.be(100);
-        expect(markings[0].yaxis.to).to.be(200);
-        expect(markings[0].color).to.be('#111');
-        expect(markings[1].yaxis.from).to.be(200);
-        expect(markings[1].yaxis.to).to.be(Infinity);
-      });
-    });
-
-    graphScenario('inverted grid thresholds 200, 100', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.panel.grid = {
-          threshold1: 200,
-          threshold1Color: "#111",
-          threshold2: 100,
-          threshold2Color: "#222",
-        };
-      });
-
-      it('should add grid markings', function() {
-        var markings = ctx.plotOptions.grid.markings;
-        expect(markings[0].yaxis.from).to.be(200);
-        expect(markings[0].yaxis.to).to.be(100);
-        expect(markings[0].color).to.be('#111');
-        expect(markings[1].yaxis.from).to.be(100);
-        expect(markings[1].yaxis.to).to.be(-Infinity);
-      });
-    });
-
-    graphScenario('grid thresholds from zero', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.panel.grid = {
-          threshold1: 0,
-          threshold1Color: "#111",
-        };
-      });
-
-      it('should add grid markings', function() {
-        var markings = ctx.plotOptions.grid.markings;
-        expect(markings[0].yaxis.from).to.be(0);
-      });
-    });
-
-    graphScenario('when logBase is log 10', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.panel.grid = {
-          leftMax: null,
-          rightMax: null,
-          leftMin: null,
-          rightMin: null,
-          leftLogBase: 10,
-        };
-      });
-
-      it('should apply axis transform and ticks', function() {
-        var axis = ctx.plotOptions.yaxes[0];
-        expect(axis.transform(100)).to.be(Math.log(100+0.1));
-        expect(axis.ticks[0]).to.be(0);
-        expect(axis.ticks[1]).to.be(1);
-      });
-    });
-
-    graphScenario('should use timeStep for barWidth', function(ctx) {
-      ctx.setup(function(scope, data) {
-        scope.panel.bars = true;
-        data[0] = new TimeSeries({
-          datapoints: [[1,10],[2,20]],
-          alias: 'series1',
-        });
-      });
-
-      it('should set barWidth', function() {
-        expect(ctx.plotOptions.series.bars.barWidth).to.be(10/1.5);
-      });
-    });
-
-    graphScenario('series option overrides, fill & points', function(ctx) {
-      ctx.setup(function(scope, data) {
-        scope.panel.lines = true;
-        scope.panel.fill = 5;
-        scope.panel.seriesOverrides = [
-          { alias: 'test', fill: 0, points: true }
-        ];
-
-        data[1].alias = 'test';
-      });
-
-      it('should match second series and fill zero, and enable points', function() {
-        expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
-        expect(ctx.plotData[1].lines.fill).to.be(0.001);
-        expect(ctx.plotData[1].points.show).to.be(true);
-      });
-    });
-
-    graphScenario('should order series order according to zindex', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.panel.seriesOverrides = [{ alias: 'series1', zindex: 2 }];
-      });
-
-      it('should move zindex 2 last', function() {
-        expect(ctx.plotData[0].alias).to.be('series2');
-        expect(ctx.plotData[1].alias).to.be('series1');
-      });
-    });
-
-    graphScenario('when series is hidden', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.hiddenSeries = {'series2': true};
-      });
-
-      it('should remove datapoints and disable stack', function() {
-        expect(ctx.plotData[0].alias).to.be('series1');
-        expect(ctx.plotData[1].data.length).to.be(0);
-        expect(ctx.plotData[1].stack).to.be(false);
-      });
-    });
-
-    graphScenario('when stack and percent', function(ctx) {
-      ctx.setup(function(scope) {
-        scope.panel.percentage = true;
-        scope.panel.stack = true;
-      });
-
-      it('should show percentage', function() {
-        var axis = ctx.plotOptions.yaxes[0];
-        expect(axis.tickFormatter(100, axis)).to.be("100%");
-      });
-    });
-  });
-});
-

+ 0 - 171
public/test/specs/graph-tooltip-specs.js

@@ -1,171 +0,0 @@
-define([
-  'jquery',
-  'app/plugins/panel/graph/graph.tooltip'
-], function($, GraphTooltip) {
-  'use strict';
-
-  var scope =  {
-    appEvent: sinon.spy(),
-    onAppEvent: sinon.spy(),
-  };
-
-  var elem = $('<div></div>');
-  var dashboard = { };
-
-  function describeSharedTooltip(desc, fn) {
-    var ctx = {};
-    ctx.scope = scope;
-    ctx.scope.panel =  {
-      tooltip:  {
-        shared: true
-      },
-      legend: { },
-      stack: false
-    };
-
-    ctx.setup = function(setupFn) {
-      ctx.setupFn = setupFn;
-    };
-
-    describe(desc, function() {
-      beforeEach(function() {
-        ctx.setupFn();
-        var tooltip = new GraphTooltip(elem, dashboard, scope);
-        ctx.results = tooltip.getMultiSeriesPlotHoverInfo(ctx.data, ctx.pos);
-      });
-
-      fn(ctx);
-    });
-  }
-
-  describeSharedTooltip("steppedLine false, stack false", function(ctx) {
-    ctx.setup(function() {
-      ctx.data = [
-        { data: [[10, 15], [12, 20]], lines: {} },
-        { data: [[10, 2], [12, 3]], lines: {} }
-      ];
-      ctx.pos = { x: 11 };
-    });
-
-    it('should return 2 series', function() {
-      expect(ctx.results.length).to.be(2);
-    });
-    it('should add time to results array', function() {
-      expect(ctx.results.time).to.be(10);
-    });
-    it('should set value and hoverIndex', function() {
-      expect(ctx.results[0].value).to.be(15);
-      expect(ctx.results[1].value).to.be(2);
-      expect(ctx.results[0].hoverIndex).to.be(0);
-    });
-  });
-
-  describeSharedTooltip("one series is hidden", function(ctx) {
-    ctx.setup(function() {
-      ctx.data = [
-        { data: [[10, 15], [12, 20]], },
-        { data: [] }
-      ];
-      ctx.pos = { x: 11 };
-    });
-  });
-
-  describeSharedTooltip("steppedLine false, stack true, individual false", function(ctx) {
-    ctx.setup(function() {
-      ctx.data = [
-        {
-          data: [[10, 15], [12, 20]],
-          lines: {},
-          datapoints: {
-            pointsize: 2,
-            points: [[10,15], [12,20]],
-          },
-          stack: true,
-        },
-        {
-          data: [[10, 2], [12, 3]],
-          lines: {},
-          datapoints: {
-            pointsize: 2,
-            points: [[10, 2], [12, 3]],
-          },
-          stack: true
-        }
-      ];
-      ctx.scope.panel.stack = true;
-      ctx.pos = { x: 11 };
-    });
-
-    it('should show stacked value', function() {
-      expect(ctx.results[1].value).to.be(17);
-    });
-  });
-
-  describeSharedTooltip("steppedLine false, stack true, individual false, series stack false", function(ctx) {
-    ctx.setup(function() {
-      ctx.data = [
-        {
-          data: [[10, 15], [12, 20]],
-          lines: {},
-          datapoints: {
-            pointsize: 2,
-            points: [[10, 15], [12, 20]],
-          },
-          stack: true
-        },
-        {
-          data: [[10, 2], [12, 3]],
-          lines: {},
-          datapoints: {
-            pointsize: 2,
-            points: [[10, 2], [12, 3]],
-          },
-          stack: false
-        }
-      ];
-      ctx.scope.panel.stack = true;
-      ctx.pos = { x: 11 };
-    });
-
-    it('should not show stacked value', function() {
-      expect(ctx.results[1].value).to.be(2);
-    });
-
-  });
-
-  describeSharedTooltip("steppedLine false, stack true, individual true", function(ctx) {
-    ctx.setup(function() {
-      ctx.data = [
-        {
-          data: [[10, 15], [12, 20]],
-          lines: {},
-          datapoints: {
-            pointsize: 2,
-            points: [[10, 15], [12, 20]],
-          },
-          stack: true
-        },
-        {
-          data: [[10, 2], [12, 3]],
-          lines: {},
-          datapoints: {
-            pointsize: 2,
-            points: [[10, 2], [12, 3]],
-          },
-          stack: false
-        }
-      ];
-      ctx.scope.panel.stack = true;
-      ctx.scope.panel.tooltip.value_type = 'individual';
-      ctx.pos = { x: 11 };
-    });
-
-    it('should not show stacked value', function() {
-      expect(ctx.results[1].value).to.be(2);
-    });
-
-  });
-
-});
-
-

+ 2 - 4
public/test/specs/helpers.d.ts

@@ -1,6 +1,4 @@
-declare module "test/specs/helpers" {
-  let helpers: any;
-  export default helpers;
-}
+declare let helpers: any;
+export default helpers;