Просмотр исходного кода

Merge branch 'master' of github.com:grafana/grafana

Torkel Ödegaard 8 лет назад
Родитель
Сommit
6ea99540ec

+ 9 - 1
public/app/plugins/panel/singlestat/editor.html

@@ -3,11 +3,19 @@
     <h5 class="section-heading">Value</h5>
     <h5 class="section-heading">Value</h5>
 
 
     <div class="gf-form-inline">
     <div class="gf-form-inline">
-      <div class="gf-form">
+      <div class="gf-form" ng-show="ctrl.dataType === 'timeseries'">
         <label class="gf-form-label width-6">Stat</label>
         <label class="gf-form-label width-6">Stat</label>
         <div class="gf-form-select-wrapper width-7">
         <div class="gf-form-select-wrapper width-7">
           <select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ctrl.valueNameOptions" ng-change="ctrl.render()"></select>
           <select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ctrl.valueNameOptions" ng-change="ctrl.render()"></select>
         </div>
         </div>
+      </div>
+      <div class="gf-form" ng-show="ctrl.dataType === 'table'">
+        <label class="gf-form-label width-6">Column</label>
+        <div class="gf-form-select-wrapper width-7">
+          <select class="gf-form-input" ng-model="ctrl.panel.tableColumn" ng-options="f for f in ctrl.tableColumnOptions" ng-change="ctrl.refresh()"></select>
+        </div>
+      </div>
+      <div class="gf-form">
         <label class="gf-form-label width-6">Font size</label>
         <label class="gf-form-label width-6">Font size</label>
         <div class="gf-form-select-wrapper">
         <div class="gf-form-select-wrapper">
           <select class="gf-form-input" ng-model="ctrl.panel.valueFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select>
           <select class="gf-form-input" ng-model="ctrl.panel.valueFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select>

+ 90 - 16
public/app/plugins/panel/singlestat/module.ts

@@ -14,6 +14,7 @@ import {MetricsPanelCtrl} from 'app/plugins/sdk';
 class SingleStatCtrl extends MetricsPanelCtrl {
 class SingleStatCtrl extends MetricsPanelCtrl {
   static templateUrl = 'module.html';
   static templateUrl = 'module.html';
 
 
+  dataType = 'timeseries';
   series: any[];
   series: any[];
   data: any;
   data: any;
   fontSizes: any[];
   fontSizes: any[];
@@ -22,6 +23,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
   panel: any;
   panel: any;
   events: any;
   events: any;
   valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range'];
   valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range'];
+  tableColumnOptions: any;
 
 
   // Set and populate defaults
   // Set and populate defaults
   panelDefaults = {
   panelDefaults = {
@@ -67,7 +69,8 @@ class SingleStatCtrl extends MetricsPanelCtrl {
       maxValue: 100,
       maxValue: 100,
       thresholdMarkers: true,
       thresholdMarkers: true,
       thresholdLabels: false
       thresholdLabels: false
-    }
+    },
+    tableColumn: ''
   };
   };
 
 
   /** @ngInject */
   /** @ngInject */
@@ -98,11 +101,16 @@ class SingleStatCtrl extends MetricsPanelCtrl {
   }
   }
 
 
   onDataReceived(dataList) {
   onDataReceived(dataList) {
-    this.series = dataList.map(this.seriesHandler.bind(this));
-
-    var data: any = {};
-    this.setValues(data);
-
+    const data: any = {};
+    if (dataList.length > 0 && dataList[0].type === 'table'){
+      this.dataType = 'table';
+      const tableData = dataList.map(this.tableHandler.bind(this));
+      this.setTableValues(tableData, data);
+    } else {
+      this.dataType = 'timeseries';
+      this.series = dataList.map(this.seriesHandler.bind(this));
+      this.setValues(data);
+    }
     this.data = data;
     this.data = data;
     this.render();
     this.render();
   }
   }
@@ -117,6 +125,69 @@ class SingleStatCtrl extends MetricsPanelCtrl {
     return series;
     return series;
   }
   }
 
 
+  tableHandler(tableData) {
+    const datapoints = [];
+    const columnNames = {};
+
+    tableData.columns.forEach((column, columnIndex) => {
+      columnNames[columnIndex] = column.text;
+    });
+
+    this.tableColumnOptions = columnNames;
+    if (!_.find(tableData.columns, ['text', this.panel.tableColumn])) {
+      this.setTableColumnToSensibleDefault(tableData);
+    }
+
+    tableData.rows.forEach((row) => {
+      const datapoint = {};
+
+      row.forEach((value, columnIndex) => {
+        const key = columnNames[columnIndex];
+        datapoint[key] = value;
+      });
+
+      datapoints.push(datapoint);
+    });
+
+    return datapoints;
+  }
+
+  setTableColumnToSensibleDefault(tableData) {
+    if (this.tableColumnOptions.length === 1) {
+      this.panel.tableColumn = this.tableColumnOptions[0];
+    } else {
+      this.panel.tableColumn = _.find(tableData.columns, (col) => { return col.type !== 'time'; }).text;
+    }
+  }
+
+  setTableValues(tableData, data) {
+    if (!tableData || tableData.length === 0) {
+      return;
+    }
+
+    if (tableData[0].length === 0 || !tableData[0][0][this.panel.tableColumn]) {
+      return;
+    }
+
+    let highestValue = 0;
+    let lowestValue = Number.MAX_VALUE;
+    const datapoint = tableData[0][0];
+    data.value = datapoint[this.panel.tableColumn];
+
+    if (_.isString(data.value)) {
+      data.valueFormatted = _.escape(data.value);
+      data.value = 0;
+      data.valueRounded = 0;
+    } else {
+      const decimalInfo = this.getDecimalsForValue(data.value);
+      const formatFunc = kbn.valueFormats[this.panel.format];
+      data.valueFormatted = formatFunc(datapoint[this.panel.tableColumn], decimalInfo.decimals, decimalInfo.scaledDecimals);
+      data.valueRounded = kbn.roundValue(data.value, this.panel.decimals || 0);
+    }
+
+    this.setValueMapping(data);
+  }
+
   setColoring(options) {
   setColoring(options) {
     if (options.background) {
     if (options.background) {
       this.panel.colorValue = false;
       this.panel.colorValue = false;
@@ -192,10 +263,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
       if (this.panel.valueName === 'name') {
       if (this.panel.valueName === 'name') {
         data.value = 0;
         data.value = 0;
         data.valueRounded = 0;
         data.valueRounded = 0;
-        data.valueFormated = this.series[0].alias;
+        data.valueFormatted = this.series[0].alias;
       } else if (_.isString(lastValue)) {
       } else if (_.isString(lastValue)) {
         data.value = 0;
         data.value = 0;
-        data.valueFormated = _.escape(lastValue);
+        data.valueFormatted = _.escape(lastValue);
         data.valueRounded = 0;
         data.valueRounded = 0;
       } else {
       } else {
         data.value = this.series[0].stats[this.panel.valueName];
         data.value = this.series[0].stats[this.panel.valueName];
@@ -203,7 +274,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
 
 
         var decimalInfo = this.getDecimalsForValue(data.value);
         var decimalInfo = this.getDecimalsForValue(data.value);
         var formatFunc = kbn.valueFormats[this.panel.format];
         var formatFunc = kbn.valueFormats[this.panel.format];
-        data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
+        data.valueFormatted = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
         data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
         data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
       }
       }
 
 
@@ -211,7 +282,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
       data.scopedVars = _.extend({}, this.panel.scopedVars);
       data.scopedVars = _.extend({}, this.panel.scopedVars);
       data.scopedVars["__name"] = {value: this.series[0].label};
       data.scopedVars["__name"] = {value: this.series[0].label};
     }
     }
+    this.setValueMapping(data);
+  }
 
 
+  setValueMapping(data) {
     // check value to text mappings if its enabled
     // check value to text mappings if its enabled
     if (this.panel.mappingType === 1) {
     if (this.panel.mappingType === 1) {
       for (let i = 0; i < this.panel.valueMaps.length; i++) {
       for (let i = 0; i < this.panel.valueMaps.length; i++) {
@@ -219,7 +293,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
         // special null case
         // special null case
         if (map.value === 'null') {
         if (map.value === 'null') {
           if (data.value === null || data.value === void 0) {
           if (data.value === null || data.value === void 0) {
-            data.valueFormated = map.text;
+            data.valueFormatted = map.text;
             return;
             return;
           }
           }
           continue;
           continue;
@@ -228,7 +302,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
         // value/number to text mapping
         // value/number to text mapping
         var value = parseFloat(map.value);
         var value = parseFloat(map.value);
         if (value === data.valueRounded) {
         if (value === data.valueRounded) {
-          data.valueFormated = map.text;
+          data.valueFormatted = map.text;
           return;
           return;
         }
         }
       }
       }
@@ -238,7 +312,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
         // special null case
         // special null case
         if (map.from === 'null' && map.to === 'null') {
         if (map.from === 'null' && map.to === 'null') {
           if (data.value === null || data.value === void 0) {
           if (data.value === null || data.value === void 0) {
-            data.valueFormated = map.text;
+            data.valueFormatted = map.text;
             return;
             return;
           }
           }
           continue;
           continue;
@@ -248,14 +322,14 @@ class SingleStatCtrl extends MetricsPanelCtrl {
         var from = parseFloat(map.from);
         var from = parseFloat(map.from);
         var to = parseFloat(map.to);
         var to = parseFloat(map.to);
         if (to >= data.valueRounded && from <= data.valueRounded) {
         if (to >= data.valueRounded && from <= data.valueRounded) {
-          data.valueFormated = map.text;
+          data.valueFormatted = map.text;
           return;
           return;
         }
         }
       }
       }
     }
     }
 
 
     if (data.value === null || data.value === void 0) {
     if (data.value === null || data.value === void 0) {
-      data.valueFormated = "no value";
+      data.valueFormatted = "no value";
     }
     }
   }
   }
 
 
@@ -317,7 +391,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
 
 
       if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.prefix); }
       if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.prefix); }
 
 
-      var value = applyColoringThresholds(data.value, data.valueFormated);
+      var value = applyColoringThresholds(data.value, data.valueFormatted);
       body += getSpan('singlestat-panel-value', panel.valueFontSize, value);
       body += getSpan('singlestat-panel-value', panel.valueFontSize, value);
 
 
       if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); }
       if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); }
@@ -329,7 +403,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
 
 
     function getValueText() {
     function getValueText() {
       var result = panel.prefix ? panel.prefix : '';
       var result = panel.prefix ? panel.prefix : '';
-      result += data.valueFormated;
+      result += data.valueFormatted;
       result += panel.postfix ? panel.postfix : '';
       result += panel.postfix ? panel.postfix : '';
 
 
       return result;
       return result;

+ 0 - 131
public/app/plugins/panel/singlestat/specs/singlestat-specs.ts

@@ -1,131 +0,0 @@
-///<reference path="../../../../headers/common.d.ts" />
-
-import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
-
-import angular from 'angular';
-import helpers from '../../../../../test/specs/helpers';
-import {SingleStatCtrl} from '../module';
-
-describe('SingleStatCtrl', function() {
-  var ctx = new helpers.ControllerTestContext();
-
-  function singleStatScenario(desc, func) {
-
-    describe(desc, function() {
-
-      ctx.setup = function (setupFunc) {
-
-        beforeEach(angularMocks.module('grafana.services'));
-        beforeEach(angularMocks.module('grafana.controllers'));
-        beforeEach(angularMocks.module(function($compileProvider) {
-          $compileProvider.preAssignBindingsEnabled(true);
-        }));
-
-        beforeEach(ctx.providePhase());
-        beforeEach(ctx.createPanelController(SingleStatCtrl));
-
-        beforeEach(function() {
-          setupFunc();
-          var data = [
-            {target: 'test.cpu1', datapoints: ctx.datapoints}
-          ];
-
-          ctx.ctrl.onDataReceived(data);
-          ctx.data = ctx.ctrl.data;
-        });
-      };
-
-      func(ctx);
-    });
-  }
-
-  singleStatScenario('with defaults', function(ctx) {
-    ctx.setup(function() {
-      ctx.datapoints = [[10,1], [20,2]];
-    });
-
-    it('Should use series avg as default main value', function() {
-      expect(ctx.data.value).to.be(15);
-      expect(ctx.data.valueRounded).to.be(15);
-    });
-
-    it('should set formated falue', function() {
-      expect(ctx.data.valueFormated).to.be('15');
-    });
-  });
-
-  singleStatScenario('showing serie name instead of value', function(ctx) {
-    ctx.setup(function() {
-      ctx.datapoints = [[10,1], [20,2]];
-      ctx.ctrl.panel.valueName = 'name';
-    });
-
-    it('Should use series avg as default main value', function() {
-      expect(ctx.data.value).to.be(0);
-      expect(ctx.data.valueRounded).to.be(0);
-    });
-
-    it('should set formated falue', function() {
-      expect(ctx.data.valueFormated).to.be('test.cpu1');
-    });
-  });
-
-  singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
-    ctx.setup(function() {
-      ctx.datapoints = [[99.999,1], [99.99999,2]];
-    });
-
-    it('Should be rounded', function() {
-      expect(ctx.data.value).to.be(99.999495);
-      expect(ctx.data.valueRounded).to.be(100);
-    });
-
-    it('should set formated falue', function() {
-      expect(ctx.data.valueFormated).to.be('100');
-    });
-  });
-
-  singleStatScenario('When value to text mapping is specified', function(ctx) {
-    ctx.setup(function() {
-      ctx.datapoints = [[9.9,1]];
-      ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
-    });
-
-    it('value should remain', function() {
-      expect(ctx.data.value).to.be(9.9);
-    });
-
-    it('round should be rounded up', function() {
-      expect(ctx.data.valueRounded).to.be(10);
-    });
-
-    it('Should replace value with text', function() {
-      expect(ctx.data.valueFormated).to.be('OK');
-    });
-  });
-
-  singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) {
-    ctx.setup(function() {
-      ctx.datapoints = [[41,50]];
-      ctx.ctrl.panel.mappingType = 2;
-      ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
-    });
-
-    it('Should replace value with text OK', function() {
-      expect(ctx.data.valueFormated).to.be('OK');
-    });
-  });
-
-  singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
-    ctx.setup(function() {
-      ctx.datapoints = [[65,75]];
-      ctx.ctrl.panel.mappingType = 2;
-      ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
-    });
-
-    it('Should replace value with text NOT OK', function() {
-      expect(ctx.data.valueFormated).to.be('NOT OK');
-    });
-  });
-
-});

+ 260 - 0
public/app/plugins/panel/singlestat/specs/singlestat_specs.ts

@@ -0,0 +1,260 @@
+///<reference path="../../../../headers/common.d.ts" />
+
+import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
+
+import angular from 'angular';
+import helpers from '../../../../../test/specs/helpers';
+import {SingleStatCtrl} from '../module';
+
+describe('SingleStatCtrl', function() {
+  var ctx = new helpers.ControllerTestContext();
+
+  function singleStatScenario(desc, func) {
+
+    describe(desc, function() {
+
+      ctx.setup = function (setupFunc) {
+
+        beforeEach(angularMocks.module('grafana.services'));
+        beforeEach(angularMocks.module('grafana.controllers'));
+        beforeEach(angularMocks.module(function($compileProvider) {
+          $compileProvider.preAssignBindingsEnabled(true);
+        }));
+
+        beforeEach(ctx.providePhase());
+        beforeEach(ctx.createPanelController(SingleStatCtrl));
+
+        beforeEach(function() {
+          setupFunc();
+          ctx.ctrl.onDataReceived(ctx.data);
+          ctx.data = ctx.ctrl.data;
+        });
+      };
+
+      func(ctx);
+    });
+  }
+
+  singleStatScenario('with defaults', function(ctx) {
+    ctx.setup(function() {
+      ctx.data = [
+        {target: 'test.cpu1', datapoints: [[10,1], [20,2]]}
+      ];
+    });
+
+    it('Should use series avg as default main value', function() {
+      expect(ctx.data.value).to.be(15);
+      expect(ctx.data.valueRounded).to.be(15);
+    });
+
+    it('should set formatted falue', function() {
+      expect(ctx.data.valueFormatted).to.be('15');
+    });
+  });
+
+  singleStatScenario('showing serie name instead of value', function(ctx) {
+    ctx.setup(function() {
+       ctx.data = [
+        {target: 'test.cpu1', datapoints: [[10,1], [20,2]]}
+       ];
+      ctx.ctrl.panel.valueName = 'name';
+    });
+
+    it('Should use series avg as default main value', function() {
+      expect(ctx.data.value).to.be(0);
+      expect(ctx.data.valueRounded).to.be(0);
+    });
+
+    it('should set formatted falue', function() {
+      expect(ctx.data.valueFormatted).to.be('test.cpu1');
+    });
+  });
+
+  singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
+    ctx.setup(function() {
+      ctx.data = [
+        {target: 'test.cpu1', datapoints: [[99.999,1], [99.99999,2]]}
+      ];
+    });
+
+    it('Should be rounded', function() {
+      expect(ctx.data.value).to.be(99.999495);
+      expect(ctx.data.valueRounded).to.be(100);
+    });
+
+    it('should set formatted falue', function() {
+      expect(ctx.data.valueFormatted).to.be('100');
+    });
+  });
+
+  singleStatScenario('When value to text mapping is specified', function(ctx) {
+    ctx.setup(function() {
+      ctx.data = [
+        {target: 'test.cpu1', datapoints: [[9.9,1]]}
+      ];
+      ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
+    });
+
+    it('value should remain', function() {
+      expect(ctx.data.value).to.be(9.9);
+    });
+
+    it('round should be rounded up', function() {
+      expect(ctx.data.valueRounded).to.be(10);
+    });
+
+    it('Should replace value with text', function() {
+      expect(ctx.data.valueFormatted).to.be('OK');
+    });
+  });
+
+  singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) {
+    ctx.setup(function() {
+      ctx.data = [
+        {target: 'test.cpu1', datapoints: [[41,50]]}
+      ];
+      ctx.ctrl.panel.mappingType = 2;
+      ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
+    });
+
+    it('Should replace value with text OK', function() {
+      expect(ctx.data.valueFormatted).to.be('OK');
+    });
+  });
+
+  singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
+    ctx.setup(function() {
+      ctx.data = [
+        {target: 'test.cpu1', datapoints: [[65,75]]}
+      ];
+      ctx.ctrl.panel.mappingType = 2;
+      ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
+    });
+
+    it('Should replace value with text NOT OK', function() {
+      expect(ctx.data.valueFormatted).to.be('NOT OK');
+    });
+  });
+
+  describe('When table data', function() {
+    const tableData = [{
+      "columns": [
+        { "text": "Time", "type": "time" },
+        { "text": "test1" },
+        { "text": "mean" },
+        { "text": "test2" }
+      ],
+      "rows": [
+        [1492759673649, 'ignore1', 15, 'ignore2']
+      ],
+      "type": "table"
+    }];
+
+    singleStatScenario('with default values', function(ctx) {
+      ctx.setup(function() {
+        ctx.data = tableData;
+        ctx.ctrl.panel.tableColumn = 'mean';
+      });
+
+      it('Should use first rows value as default main value', function() {
+        expect(ctx.data.value).to.be(15);
+        expect(ctx.data.valueRounded).to.be(15);
+      });
+
+      it('should set formatted value', function() {
+        expect(ctx.data.valueFormatted).to.be('15');
+      });
+    });
+
+    singleStatScenario('When table data has multiple columns', function(ctx) {
+      ctx.setup(function() {
+        ctx.data = tableData;
+        ctx.ctrl.panel.tableColumn = '';
+      });
+
+      it('Should set column to first column that is not time', function() {
+        expect(ctx.ctrl.panel.tableColumn).to.be('test1');
+      });
+    });
+
+    singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
+      ctx.setup(function() {
+        ctx.data = tableData;
+        ctx.data[0].rows[0] = [1492759673649,'ignore1', 99.99999, 'ignore2'];
+        ctx.ctrl.panel.tableColumn = 'mean';
+      });
+
+      it('Should be rounded', function() {
+        expect(ctx.data.value).to.be(99.99999);
+        expect(ctx.data.valueRounded).to.be(100);
+      });
+
+      it('should set formatted falue', function() {
+        expect(ctx.data.valueFormatted).to.be('100');
+      });
+    });
+
+    singleStatScenario('When value to text mapping is specified', function(ctx) {
+      ctx.setup(function() {
+        ctx.data = tableData;
+        ctx.data[0].rows[0] = [1492759673649,'ignore1', 9.9, 'ignore2'];
+        ctx.ctrl.panel.tableColumn = 'mean';
+        ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
+      });
+
+      it('value should remain', function() {
+        expect(ctx.data.value).to.be(9.9);
+      });
+
+      it('round should be rounded up', function() {
+        expect(ctx.data.valueRounded).to.be(10);
+      });
+
+      it('Should replace value with text', function() {
+        expect(ctx.data.valueFormatted).to.be('OK');
+      });
+    });
+
+    singleStatScenario('When range to text mapping is specified for first range', function(ctx) {
+      ctx.setup(function() {
+        ctx.data = tableData;
+        ctx.data[0].rows[0] = [1492759673649,'ignore1', 41, 'ignore2'];
+        ctx.ctrl.panel.tableColumn = 'mean';
+        ctx.ctrl.panel.mappingType = 2;
+        ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
+      });
+
+      it('Should replace value with text OK', function() {
+        expect(ctx.data.valueFormatted).to.be('OK');
+      });
+    });
+
+    singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
+      ctx.setup(function() {
+        ctx.data = tableData;
+        ctx.data[0].rows[0] = [1492759673649,'ignore1', 65, 'ignore2'];
+        ctx.ctrl.panel.tableColumn = 'mean';
+        ctx.ctrl.panel.mappingType = 2;
+        ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
+      });
+
+      it('Should replace value with text NOT OK', function() {
+        expect(ctx.data.valueFormatted).to.be('NOT OK');
+      });
+    });
+
+    singleStatScenario('When value is string', function(ctx) {
+      ctx.setup(function() {
+        ctx.data = tableData;
+        ctx.data[0].rows[0] = [1492759673649,'ignore1', 65, 'ignore2'];
+        ctx.ctrl.panel.tableColumn = 'test1';
+      });
+
+      it('Should replace value with text NOT OK', function() {
+        expect(ctx.data.valueFormatted).to.be('ignore1');
+      });
+    });
+  });
+});
+
+