AMBARI-18159 Cover widget views with unit tests. (atkach)

Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/79775234
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/79775234
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/79775234

Branch: refs/heads/branch-dev-logsearch
Commit: 797752341c83b38cedfbad3b89c61736eac69772
Parents: e2fd231
Author: Andrii Tkach <atk...@apache.org>
Authored: Tue Aug 16 13:08:50 2016 +0300
Committer: Andrii Tkach <atk...@apache.org>
Committed: Tue Aug 16 17:37:03 2016 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   2 +
 .../views/common/widget/gauge_widget_view.js    |  12 +-
 .../views/common/widget/graph_widget_view.js    |   6 +-
 .../views/common/widget/heatmap_widget_view.js  |   7 +-
 .../common/widget/gauge_widget_view_test.js     | 152 ++++-
 .../common/widget/graph_widget_view_test.js     | 552 ++++++++++++++++++-
 .../common/widget/heatmap_widget_view_test.js   | 268 +++++++++
 .../common/widget/number_widget_view_test.js    |  26 +
 .../common/widget/template_widget_view_test.js  |  44 ++
 9 files changed, 1053 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js 
b/ambari-web/app/assets/test/tests.js
index 00aabff..364f2ed 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -238,6 +238,8 @@ var files = [
   'test/views/common/widget/graph_widget_view_test',
   'test/views/common/widget/number_widget_view_test',
   'test/views/common/widget/gauge_widget_view_test',
+  'test/views/common/widget/template_widget_view_test',
+  'test/views/common/widget/heatmap_widget_view_test',
   'test/views/common/modal_popups/cluster_check_popup_test',
   'test/views/common/modal_popups/hosts_table_list_popup_test',
   'test/views/common/modal_popups/dependent_configs_list_popup_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/app/views/common/widget/gauge_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/gauge_widget_view.js 
b/ambari-web/app/views/common/widget/gauge_widget_view.js
index a0bdaea..42224fe 100644
--- a/ambari-web/app/views/common/widget/gauge_widget_view.js
+++ b/ambari-web/app/views/common/widget/gauge_widget_view.js
@@ -87,7 +87,7 @@ App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
     centerTextColor: Em.computed.alias('contentColor'),
 
     palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
+      scheme: ['#FFFFFF', '#D6DDDF'].reverse()
     }),
 
     data: function () {
@@ -105,17 +105,17 @@ App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
       var color_orange = App.healthStatusOrange;
       if ((isNaN(threshold1) && isNaN(threshold2)) || (isNaN(threshold1) && 
used <= threshold2) || (isNaN(threshold2) && used <= threshold1) || 
(!isNaN(threshold2) && (threshold1 > threshold2) && (used > threshold1)) || 
(!isNaN(threshold2) && (threshold1 < threshold2) && (used <= threshold1))) {
         this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
+          scheme: ['#FFFFFF', color_green].reverse()
         }));
         return color_green;
       } else if ((!isNaN(threshold2) && used.isInRange(threshold1, 
threshold2)) || (isNaN(threshold2) && used > threshold1)) {
         this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
+          scheme: ['#FFFFFF', color_orange].reverse()
         }));
         return color_orange;
       } else {
         this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
+          scheme: ['#FFFFFF', color_red].reverse()
         }));
         return color_red;
       }
@@ -124,8 +124,8 @@ App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
     // refresh text and color when data in model changed
     refreshSvg: function () {
       // remove old svg
-      var old_svg =  $("#" + this.get('id'));
-      if(old_svg){
+      var old_svg = $("#" + this.get('id'));
+      if (old_svg) {
         old_svg.remove();
       }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/app/views/common/widget/graph_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/graph_widget_view.js 
b/ambari-web/app/views/common/widget/graph_widget_view.js
index 2413236..6348077 100644
--- a/ambari-web/app/views/common/widget/graph_widget_view.js
+++ b/ambari-web/app/views/common/widget/graph_widget_view.js
@@ -20,6 +20,8 @@ var App = require('app');
 
 var fileUtils = require('utils/file_utils');
 
+var CUSTOM_TIME_INDEX = 8;
+
 App.GraphWidgetView = Em.View.extend(App.WidgetMixin, App.ExportMetricsMixin, {
   templateName: require('templates/common/widget/graph_widget'),
 
@@ -59,7 +61,7 @@ App.GraphWidgetView = Em.View.extend(App.WidgetMixin, 
App.ExportMetricsMixin, {
     }
 
     // Custom start and end time is specified by user
-    if (this.get('exportTargetView.currentTimeIndex') === 8) {
+    if (this.get('exportTargetView.currentTimeIndex') === CUSTOM_TIME_INDEX) {
       return 0;
     }
 
@@ -290,7 +292,7 @@ App.GraphWidgetView = Em.View.extend(App.WidgetMixin, 
App.ExportMetricsMixin, {
      */
     setTimeRange: function () {
       if (this.get('isPopup')) {
-        if (this.get('currentTimeIndex') === 8) {
+        if (this.get('currentTimeIndex') === CUSTOM_TIME_INDEX) {
           // Custom start and end time is specified by user
           this.get('parentView').propertyDidChange('customTimeRange');
         } else {

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/app/views/common/widget/heatmap_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/heatmap_widget_view.js 
b/ambari-web/app/views/common/widget/heatmap_widget_view.js
index 6c74420..a7a919b 100644
--- a/ambari-web/app/views/common/widget/heatmap_widget_view.js
+++ b/ambari-web/app/views/common/widget/heatmap_widget_view.js
@@ -105,6 +105,7 @@ App.HeatmapWidgetView = Em.View.extend(App.WidgetMixin, {
 
   /**
    * calculate value for heatmap widgets
+   * @returns {Object}
    */
   calculateValues: function () {
     return 
this.computeExpression(this.extractExpressions(this.get('content.values')[0]), 
this.get('metrics'));
@@ -113,8 +114,8 @@ App.HeatmapWidgetView = Em.View.extend(App.WidgetMixin, {
 
   /**
    * compute expression
-   * @param expressions
-   * @param metrics
+   * @param {Array} expressions
+   * @param {Array} metrics
    * @returns {object}
    */
   computeExpression: function (expressions, metrics) {
@@ -148,7 +149,7 @@ App.HeatmapWidgetView = Em.View.extend(App.WidgetMixin, {
 
         if (validExpression && 
this.get('MATH_EXPRESSION_REGEX').test(beforeCompute)) {
           var value = Number(window.eval(beforeCompute)).toString();
-          if (value == "NaN")  {
+          if (value === "NaN")  {
             value = 0
           }
           hostToValueMap[_hostName] = value;

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/test/views/common/widget/gauge_widget_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/common/widget/gauge_widget_view_test.js 
b/ambari-web/test/views/common/widget/gauge_widget_view_test.js
index 2ba4816..c296327 100644
--- a/ambari-web/test/views/common/widget/gauge_widget_view_test.js
+++ b/ambari-web/test/views/common/widget/gauge_widget_view_test.js
@@ -21,7 +21,7 @@ require('views/common/widget/gauge_widget_view');
 
 describe('App.GaugeWidgetView', function () {
 
-  var view;
+  var view, chartView;
 
   beforeEach(function () {
     view = App.GaugeWidgetView.create({
@@ -33,6 +33,9 @@ describe('App.GaugeWidgetView', function () {
         }
       }
     });
+    chartView = view.chartView.create({
+      parentView: view
+    });
   });
 
   afterEach(function () {
@@ -40,6 +43,87 @@ describe('App.GaugeWidgetView', function () {
     view.destroy();
   });
 
+  describe("#isOverflowed", function () {
+    var testCases = [
+      {
+        value: '',
+        expected: false
+      },
+      {
+        value: '1',
+        expected: false
+      },
+      {
+        value: '0',
+        expected: false
+      },
+      {
+        value: '-1',
+        expected: true
+      },
+      {
+        value: '1.1',
+        expected: true
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("value = " + test.value, function() {
+        view.set('value', test.value);
+        view.propertyDidChange('isOverflowed');
+        expect(view.get('isOverflowed')).to.be.equal(test.expected);
+      });
+    });
+
+  });
+
+  describe("#isUnavailable", function () {
+    var testCases = [
+      {
+        value: '',
+        isOverflowed: false,
+        expected: true
+      },
+      {
+        value: 'a',
+        isOverflowed: false,
+        expected: true
+      },
+      {
+        value: 'a1',
+        isOverflowed: false,
+        expected: true
+      },
+      {
+        value: '1',
+        isOverflowed: false,
+        expected: false
+      },
+      {
+        value: '1.1',
+        isOverflowed: false,
+        expected: false
+      },
+      {
+        value: '1.1',
+        isOverflowed: true,
+        expected: true
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("value = " + test.value +
+         " isOverflowed = " + test.isOverflowed, function() {
+        view.reopen({
+          value: test.value,
+          isOverflowed: test.isOverflowed
+        });
+        view.propertyDidChange('isUnavailable');
+        expect(view.get('isUnavailable')).to.equal(test.expected);
+      });
+    });
+  });
+
   describe("#chartView.contentColor()", function() {
     var testCases = [
       {
@@ -166,11 +250,71 @@ describe('App.GaugeWidgetView', function () {
         view.set('value', test.data.value);
         view.set('content.properties.warning_threshold', 
test.data.warningThreshold);
         view.set('content.properties.error_threshold', 
test.data.criticalThreshold);
-        var chartView = view.chartView.create({
-          parentView: view
-        });
         expect(chartView.get('contentColor')).to.eql(test.result);
       });
     });
   });
+
+  describe("#chartView.data", function () {
+    var testCases = [
+      {
+        value: '',
+        expected: [0, 100]
+      },
+      {
+        value: 'a',
+        expected: [0, 100]
+      },
+      {
+        value: '1.1',
+        expected: [0, 100]
+      },
+      {
+        value: '0.1',
+        expected: ['10', 90]
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("value = " + test.value, function() {
+        view.set('value', test.value);
+        expect(chartView.get('data')).to.be.eql(test.expected);
+      });
+    });
+  });
+
+  describe("#chartView.refreshSvg", function () {
+    var container = {
+      remove: Em.K
+    };
+
+    beforeEach(function() {
+      this.mock = sinon.stub(window, '$');
+      sinon.stub(container, 'remove');
+      sinon.stub(chartView, 'appendSvg');
+    });
+
+    afterEach(function() {
+      container.remove.restore();
+      this.mock.restore();
+      chartView.appendSvg.restore();
+    });
+
+    it("appendSvg should be called", function() {
+      chartView.refreshSvg();
+      expect(chartView.appendSvg.calledOnce).to.be.true;
+    });
+
+    it("should remove old svg", function() {
+      this.mock.returns(container);
+      chartView.refreshSvg();
+      expect(container.remove.calledOnce).to.be.true;
+    });
+
+    it("chart do not have old svg", function() {
+      this.mock.returns(null);
+      chartView.refreshSvg();
+      expect(container.remove.called).to.be.false;
+    });
+  });
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/test/views/common/widget/graph_widget_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/common/widget/graph_widget_view_test.js 
b/ambari-web/test/views/common/widget/graph_widget_view_test.js
index 31c9db0..9c8458c 100644
--- a/ambari-web/test/views/common/widget/graph_widget_view_test.js
+++ b/ambari-web/test/views/common/widget/graph_widget_view_test.js
@@ -25,7 +25,12 @@ describe('App.GraphWidgetView', function () {
   var view;
 
   beforeEach(function () {
-    view = App.GraphWidgetView.create();
+    view = App.GraphWidgetView.create({
+      content: Em.Object.create({
+        properties: {}
+      }),
+      parentView: Em.Object.create()
+    });
   });
 
   afterEach(function () {
@@ -272,4 +277,549 @@ describe('App.GraphWidgetView', function () {
     });
   });
 
+  describe("#timeRange()", function () {
+    var testCases = [
+      {
+        time_range: null,
+        currentTimeIndex: 1,
+        customTimeRange: null,
+        expected: 3600
+      },
+      {
+        time_range: null,
+        currentTimeIndex: 1,
+        customTimeRange: 2,
+        expected: 2
+      },
+      {
+        time_range: null,
+        currentTimeIndex: 8,
+        customTimeRange: 2,
+        expected: 0
+      },
+      {
+        time_range: 2,
+        currentTimeIndex: 1,
+        customTimeRange: null,
+        expected: 7200
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("time_range=" + test.time_range +
+         " currentTimeIndex=" + test.currentTimeIndex +
+         " customTimeRange=" + test.customTimeRange, function() {
+        view.set('content.properties.time_range', test.time_range);
+        view.set('customTimeRange', test.customTimeRange);
+        view.reopen({
+          exportTargetView: Em.Object.create({
+            currentTimeIndex: test.currentTimeIndex
+          })
+        });
+        view.propertyDidChange('timeRange');
+        expect(view.get('timeRange')).to.be.equal(test.expected);
+      });
+    });
+  });
+
+  describe("#drawWidget()", function () {
+
+    beforeEach(function() {
+      sinon.stub(view, 'calculateValues').returns({});
+      view.set('data', null);
+    });
+
+    afterEach(function() {
+      view.calculateValues.restore();
+    });
+
+    it("isLoaded = false", function() {
+      view.set('isLoaded', false);
+      view.drawWidget();
+      expect(view.get('data')).to.be.null;
+    });
+
+    it("isLoaded = true", function() {
+      view.set('isLoaded', true);
+      view.drawWidget();
+      expect(view.get('data')).to.be.eql({});
+    });
+  });
+
+  describe("#calculateValues()", function () {
+    beforeEach(function() {
+      this.mockExtract = sinon.stub(view, 'extractExpressions');
+      this.mockCompute = sinon.stub(view, 'computeExpression');
+    });
+
+    afterEach(function() {
+      this.mockExtract.restore();
+      this.mockCompute.restore();
+    });
+
+    var testCases = [
+      {
+        metrics: {},
+        values: [],
+        expression: [],
+        computed: {},
+        expected: []
+      },
+      {
+        metrics: {},
+        values: [{}],
+        expression: [],
+        computed: {},
+        expected: []
+      },
+      {
+        metrics: {},
+        values: [{
+          value: '${m1}'
+        }],
+        expression: ['${m1}'],
+        computed: {
+          '${m1}': []
+        },
+        expected: []
+      },
+      {
+        metrics: {},
+        values: [{
+          value: '${m1}',
+          name: 'v1'
+        }],
+        expression: ['${m1}'],
+        computed: {
+          '${m1}': [{
+            m1: {}
+          }]
+        },
+        expected: [
+          {
+            name: 'v1',
+            data: [{
+              m1: {}
+            }]
+          }
+        ]
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("metrics=" + JSON.stringify(test.metrics) +
+         " values=" + JSON.stringify(test.values) +
+         " expression=" + test.expression +
+         " computed=" + test.computed, function() {
+        view.set('metrics', test.metrics);
+        view.set('content.values', test.values);
+        this.mockCompute.returns(test.computed);
+        this.mockExtract.returns(test.expression);
+        expect(view.calculateValues()).to.be.eql(test.expected);
+      });
+    });
+  });
+
+  describe("#computeExpression()", function () {
+
+    beforeEach(function() {
+      sinon.stub(view, 'adjustData', function (dataLinks) {
+        dataLinks['m1'][1] = [3, 1112];
+      });
+    });
+
+    afterEach(function() {
+      view.adjustData.restore();
+    });
+
+    var testCases = [
+      {
+        expression: '1',
+        metrics: [],
+        expected: {
+          '${1}': []
+        },
+        adjustDataCalled: false
+      },
+      {
+        expression: 'm1',
+        metrics: [],
+        expected: {
+          '${m1}': []
+        },
+        adjustDataCalled: false
+      },
+      {
+        expression: 'm1',
+        metrics: [{
+          name: 'm1',
+          data: []
+        }],
+        expected: {
+          '${m1}': []
+        },
+        adjustDataCalled: false
+      },
+      {
+        expression: 'm1',
+        metrics: [{
+          name: 'm1',
+          data: [
+            [null, 1111]
+          ]
+        }],
+        expected: {
+          '${m1}': [
+            [null, 1111]
+          ]
+        },
+        adjustDataCalled: false
+      },
+      {
+        expression: 'm1',
+        metrics: [{
+          name: 'm1',
+          data: [
+            [1, 1111]
+          ]
+        }],
+        expected: {
+          '${m1}': [
+            [1, 1111]
+          ]
+        },
+        adjustDataCalled: false
+      },
+      {
+        expression: 'm1+1',
+        metrics: [{
+          name: 'm1',
+          data: [
+            [1, 1111]
+          ]
+        }],
+        expected: {
+          '${m1+1}': [
+            [2, 1111]
+          ]
+        },
+        adjustDataCalled: false
+      },
+      {
+        expression: 'm1/m2',
+        metrics: [
+          {
+            name: 'm1',
+            data: [
+              [0, 1111]
+            ]
+          },
+          {
+            name: 'm2',
+            data: [
+              [0, 1111]
+            ]
+          }
+        ],
+        expected: {
+          '${m1/m2}': [
+            [0, 1111]
+          ]
+        },
+        adjustDataCalled: false
+      },
+      {
+        expression: 'm1+m2',
+        metrics: [
+          {
+            name: 'm1',
+            data: [
+              [1, 1111]
+            ]
+          },
+          {
+            name: 'm2',
+            data: [
+              [1, 1111],
+              [2, 1112]
+            ]
+          }],
+        expected: {
+          '${m1+m2}': [
+            [2, 1111],
+            [5, 1112]
+          ]
+        },
+        adjustDataCalled: true
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("expression=" + test.expression +
+         " metrics=" + JSON.stringify(test.metrics), function() {
+        expect(view.computeExpression(test.expression, 
test.metrics)).to.be.eql(test.expected);
+        expect(view.adjustData.calledOnce).to.be.equal(test.adjustDataCalled);
+      });
+    });
+  });
+
+  describe("#addTimeProperties()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App, 'dateTime').returns(10000);
+      view.set('timeStep', 15);
+    });
+
+    afterEach(function() {
+      App.dateTime.restore();
+    });
+
+    it("targetView is null", function() {
+      view.reopen({
+        exportTargetView: null
+      });
+      view.set('parentView', null);
+      expect(view.addTimeProperties([{}])).to.be.empty;
+    });
+
+    it("empty metricPaths", function() {
+      expect(view.addTimeProperties([])).to.be.empty;
+    });
+
+    it("timeRange=5", function() {
+      view.reopen({
+        timeRange: 5,
+        exportTargetView: Em.Object.create({
+          isPopup: true
+        })
+      });
+      expect(view.addTimeProperties(['m1'])).to.be.eql([
+        "m1[5,10,15]"
+      ]);
+    });
+
+    it("timeRange=0, customStartTime=null", function() {
+      view.reopen({
+        timeRange: 0,
+        exportTargetView: Em.Object.create({
+          isPopup: true,
+          customStartTime: null
+        })
+      });
+      expect(view.addTimeProperties(['m1'])).to.be.eql([
+        "m1[10,10,15]"
+      ]);
+    });
+
+    it("timeRange=0, customStartTime=1000, customEndTime=null", function() {
+      view.reopen({
+        timeRange: 0,
+        exportTargetView: Em.Object.create({
+          isPopup: true,
+          customStartTime: 1000,
+          customEndTime: null
+        })
+      });
+      expect(view.addTimeProperties(['m1'])).to.be.eql([
+        "m1[10,10,15]"
+      ]);
+    });
+
+    it("timeRange=0, customStartTime=1000, customEndTime=10000", function() {
+      view.reopen({
+        timeRange: 0,
+        exportTargetView: Em.Object.create({
+          isPopup: true,
+          customStartTime: 1000,
+          customEndTime: 10000
+        })
+      });
+      expect(view.addTimeProperties(['m1'])).to.be.eql([
+        "m1[1,10,15]"
+      ]);
+    });
+  });
+
+  describe("#graphView", function () {
+    var graphView;
+
+    beforeEach(function () {
+      graphView = view.get('graphView').create({
+        parentView: view,
+        _refreshGraph: Em.K,
+        $: function() {
+          return {
+            closest: function() {
+              return {on: Em.K}
+            }
+          }
+        }
+      });
+    });
+
+    describe("#setYAxisFormatter()", function () {
+
+      beforeEach(function () {
+        sinon.stub(App.ChartLinearTimeView, 'DisplayUnitFormatter');
+        graphView.set('yAxisFormatter', null);
+      });
+
+      afterEach(function () {
+        App.ChartLinearTimeView.DisplayUnitFormatter.restore();
+      });
+
+      it("yAxisFormatter should not be set", function () {
+        graphView.reopen({
+          displayUnit: null
+        });
+        graphView.setYAxisFormatter();
+        expect(graphView.get('yAxisFormatter')).to.be.null;
+      });
+
+      it("yAxisFormatter should be set", function () {
+        graphView.reopen({
+          displayUnit: 'u1'
+        });
+        graphView.setYAxisFormatter();
+        expect(graphView.get('yAxisFormatter')).to.be.function;
+      });
+    });
+
+    describe("#setTimeRange", function () {
+
+      beforeEach(function() {
+        sinon.stub(graphView.get('parentView'), 'propertyDidChange');
+      });
+
+      afterEach(function() {
+        graphView.get('parentView').propertyDidChange.restore();
+      });
+
+      it("isPopup=false", function() {
+        graphView.set('isPopup', false);
+        graphView.setTimeRange();
+        expect(graphView.get('parentView.customTimeRange')).to.be.null;
+      });
+
+      it("isPopup=true, currentTimeIndex=8", function() {
+        graphView.set('isPopup', true);
+        graphView.set('currentTimeIndex', 8);
+        graphView.setTimeRange();
+        
expect(graphView.get('parentView').propertyDidChange.calledWith('customTimeRange')).to.be.true;
+      });
+
+      it("isPopup=true, currentTimeIndex=1", function() {
+        graphView.set('isPopup', true);
+        graphView.set('currentTimeIndex', 1);
+        graphView.set('timeUnitSeconds', 10);
+        expect(graphView.get('parentView.customTimeRange')).to.be.equal(10);
+      });
+    });
+
+    describe("#id", function () {
+
+      it("should return id", function() {
+        graphView.set('parentView.content.id', 'g1');
+        graphView.propertyDidChange('id');
+        expect(graphView.get('id')).to.be.equal('widget_g1_graph');
+      });
+    });
+
+    describe("#renderer", function () {
+
+      it("should return area", function() {
+        graphView.set('parentView.content.properties.graph_type', 'STACK');
+        graphView.propertyDidChange('renderer');
+        expect(graphView.get('renderer')).to.be.equal('area');
+      });
+
+      it("should return line", function() {
+        graphView.set('parentView.content.properties.graph_type', '');
+        graphView.propertyDidChange('renderer');
+        expect(graphView.get('renderer')).to.be.equal('line');
+      });
+    });
+
+    describe("#transformToSeries()", function () {
+
+      beforeEach(function() {
+        sinon.stub(graphView, 'transformData').returns({});
+      });
+
+      afterEach(function() {
+        graphView.transformData.restore();
+      });
+
+      it("empty data", function() {
+        expect(graphView.transformToSeries([])).to.be.empty;
+      });
+
+      it("should return series", function() {
+        expect(graphView.transformToSeries([{}])).to.be.eql([{}]);
+      });
+    });
+
+    describe("#loadData()", function () {
+
+      beforeEach(function() {
+        sinon.stub(Em.run, 'next', function(context, callback) {
+          callback.apply(context);
+        });
+        sinon.stub(graphView, '_refreshGraph');
+      });
+
+      afterEach(function() {
+        Em.run.next.restore();
+        graphView._refreshGraph.restore();
+      });
+
+      it("_refreshGraph should be called", function() {
+        graphView.loadData();
+        expect(graphView._refreshGraph.calledOnce).to.be.true;
+      });
+    });
+
+    describe("#didInsertElement()", function () {
+
+      beforeEach(function() {
+        sinon.stub(graphView, 'setYAxisFormatter');
+        sinon.stub(graphView, 'loadData');
+        sinon.stub(Em.run, 'next', Em.clb);
+        sinon.stub(App, 'tooltip');
+      });
+
+      afterEach(function() {
+        graphView.setYAxisFormatter.restore();
+        graphView.loadData.restore();
+        Em.run.next.restore();
+        App.tooltip.restore();
+      });
+
+      it("setYAxisFormatter should be called", function() {
+        graphView.didInsertElement();
+        expect(graphView.setYAxisFormatter.calledOnce).to.be.true;
+      });
+
+      it("loadData should be called", function() {
+        graphView.didInsertElement();
+        expect(graphView.loadData.calledOnce).to.be.true;
+      });
+
+      it("App.tooltip should be called, isPreview=false", function() {
+        graphView.didInsertElement();
+        expect(App.tooltip.getCall(0).args[1]).to.be.eql({
+          placement: 'left',
+          template: '<div class="tooltip"><div 
class="tooltip-arrow"></div><div class="tooltip-inner 
graph-tooltip"></div></div>'
+        });
+      });
+
+      it("App.tooltip should be called, isPreview=true", function() {
+        graphView.reopen({
+          isPreview: true
+        });
+        graphView.didInsertElement();
+        expect(App.tooltip.getCall(0).args[1]).to.be.equal('disable');
+      });
+    });
+  });
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/test/views/common/widget/heatmap_widget_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/common/widget/heatmap_widget_view_test.js 
b/ambari-web/test/views/common/widget/heatmap_widget_view_test.js
new file mode 100644
index 0000000..137e664
--- /dev/null
+++ b/ambari-web/test/views/common/widget/heatmap_widget_view_test.js
@@ -0,0 +1,268 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/common/widget/heatmap_widget_view');
+
+describe('App.HeatmapWidgetView', function () {
+
+  var view;
+
+  beforeEach(function () {
+    view = App.HeatmapWidgetView.create({
+      controller: Em.Object.create(),
+      content: Em.Object.create({
+        properties: {},
+        values: []
+      })
+    });
+  });
+
+  describe("#onMetricsLoaded()", function () {
+
+    it("isLoaded false", function() {
+      view.set('isLoaded', false);
+      view.set('controller.inputMaximum', null);
+      view.set('content.properties.max_limit', 1);
+      view.onMetricsLoaded();
+      expect(view.get('controller.inputMaximum')).to.be.equal(1);
+    });
+
+    it("isLoaded true", function() {
+      view.set('isLoaded', true);
+      view.set('controller.inputMaximum', null);
+      view.set('content.properties.max_limit', 1);
+      view.onMetricsLoaded();
+      expect(view.get('controller.inputMaximum')).to.be.null
+    });
+  });
+
+  describe("#willDestroyElement()", function () {
+    var container = {
+      abort: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.stub(container, 'abort');
+      view.set('activeRequest', container);
+      view.willDestroyElement();
+    });
+
+    afterEach(function() {
+      container.abort.restore();
+    });
+
+    it("abort should be called", function() {
+      expect(container.abort.calledOnce).to.be.true;
+    });
+
+    it("activeRequest should be null", function() {
+      expect(view.get('activeRequest')).to.be.null;
+    });
+  });
+
+  describe("#loadMetrics()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App.Service, 'find').returns(Em.Object.create({
+        isStarted: false
+      }));
+      sinon.stub(view, 'onMetricsLoaded');
+    });
+
+    afterEach(function() {
+      App.Service.find.restore();
+      view.onMetricsLoaded.restore();
+    });
+
+    it("onMetricsLoaded should be called", function() {
+      view.loadMetrics();
+      expect(view.onMetricsLoaded.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#drawWidget()", function () {
+
+    beforeEach(function() {
+      sinon.stub(view, 'calculateValues').returns({});
+      sinon.stub(App.MainChartHeatmapMetric, 'create', function(obj) {
+        return obj;
+      });
+    });
+
+    afterEach(function() {
+      view.calculateValues.restore();
+      App.MainChartHeatmapMetric.create.restore();
+    });
+
+    it("isLoaded = false", function() {
+      view.set('controller.selectedMetric', null);
+      view.set('isLoaded', false);
+      view.drawWidget();
+      expect(view.get('controller.selectedMetric')).to.be.null;
+    });
+
+    it("no loaded racks", function() {
+      view.set('controller.selectedMetric', null);
+      view.set('controller.inputMaximum', 1);
+      view.setProperties({
+        isLoaded: true,
+        racks: [],
+        content: Em.Object.create({
+          displayName: 'm1',
+          properties: {
+            display_unit: 'u1'
+          }
+        })
+      });
+      view.drawWidget();
+      expect(view.get('controller.selectedMetric')).to.be.eql({
+        name: 'm1',
+        units: 'u1',
+        maximumValue: 1,
+        hostNames: [],
+        hostToValueMap: {}
+      });
+    });
+
+    it("racks loaded", function() {
+      view.set('controller.selectedMetric', null);
+      view.set('controller.inputMaximum', 1);
+      view.setProperties({
+        isLoaded: true,
+        racks: [{
+          hosts: [{
+            hostName: 'host1'
+          }],
+          isLoaded: true
+        }],
+        content: Em.Object.create({
+          displayName: 'm1',
+          properties: {
+            display_unit: 'u1'
+          }
+        })
+      });
+      view.drawWidget();
+      expect(view.get('controller.selectedMetric')).to.be.eql({
+        name: 'm1',
+        units: 'u1',
+        maximumValue: 1,
+        hostNames: ['host1'],
+        hostToValueMap: {}
+      });
+    });
+  });
+
+  describe("#calculateValues()", function () {
+
+    beforeEach(function() {
+      sinon.stub(view, 'computeExpression').returns({});
+      sinon.stub(view, 'extractExpressions');
+    });
+
+    afterEach(function() {
+      view.computeExpression.restore();
+      view.extractExpressions.restore();
+    });
+
+    it("calculateValues should return object", function() {
+      view.set('content.values', [{}]);
+      view.set('metrics', [{}]);
+      expect(view.calculateValues()).to.be.eql({});
+    });
+  });
+
+  describe("#computeExpression()", function () {
+
+    var testCases = [
+      {
+        expressions: [],
+        metrics: [],
+        expected: {}
+      },
+      {
+        expressions: ['m2'],
+        metrics: [{
+          name: 'm1',
+          hostName: 'host1'
+        }],
+        expected: {}
+      },
+      {
+        expressions: ['1'],
+        metrics: [{
+          name: 'm1',
+          hostName: 'host1'
+        }],
+        expected: {
+          'host1': '1'
+        }
+      },
+      {
+        expressions: ['m1'],
+        metrics: [{
+          name: 'm1',
+          hostName: 'host1',
+          data: '2'
+        }],
+        expected: {
+          'host1': '2'
+        }
+      },
+      {
+        expressions: ['m1+1'],
+        metrics: [{
+          name: 'm1',
+          hostName: 'host1',
+          data: '2'
+        }],
+        expected: {
+          'host1': '3'
+        }
+      },
+      {
+        expressions: ['m1'],
+        metrics: [{
+          name: 'm1',
+          hostName: 'host1',
+          data: '0/0'
+        }],
+        expected: {
+          'host1': 0
+        }
+      },
+      {
+        expressions: ['m1-m2'],
+        metrics: [{
+          name: 'm1',
+          hostName: 'host1',
+          data: '2'
+        }],
+        expected: {}
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("expressions=" + JSON.stringify(test.expressions) +
+         " metrics=" + JSON.stringify(test.metrics), function() {
+        expect(view.computeExpression(test.expressions, 
test.metrics)).to.be.eql(test.expected);
+      });
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/test/views/common/widget/number_widget_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/common/widget/number_widget_view_test.js 
b/ambari-web/test/views/common/widget/number_widget_view_test.js
index f82819e..5105bd9 100644
--- a/ambari-web/test/views/common/widget/number_widget_view_test.js
+++ b/ambari-web/test/views/common/widget/number_widget_view_test.js
@@ -40,6 +40,32 @@ describe('App.NumberWidgetView', function () {
     view.destroy();
   });
 
+  describe("#displayValue", function () {
+    var testCases = [
+      {
+        value: '',
+        expected: Em.I18n.t('common.na')
+      },
+      {
+        value: 'a',
+        expected: Em.I18n.t('common.na')
+      },
+      {
+        value: '1',
+        expected: '1u'
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("value = " + test.value, function() {
+        view.set('value', test.value);
+        view.set('content.properties.display_unit', 'u');
+        view.propertyDidChange('displayValue');
+        expect(view.get('displayValue')).to.be.equal(test.expected);
+      });
+    });
+  });
+
   describe("#contentColor()", function() {
     var testCases = [
       {

http://git-wip-us.apache.org/repos/asf/ambari/blob/79775234/ambari-web/test/views/common/widget/template_widget_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/common/widget/template_widget_view_test.js 
b/ambari-web/test/views/common/widget/template_widget_view_test.js
new file mode 100644
index 0000000..f3db9ee
--- /dev/null
+++ b/ambari-web/test/views/common/widget/template_widget_view_test.js
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/common/widget/template_widget_view');
+
+describe('App.TemplateWidgetView', function () {
+
+  var view;
+
+  beforeEach(function () {
+    view = App.TemplateWidgetView.create();
+  });
+
+  describe("#displayValue", function () {
+
+    it("empty value", function() {
+      view.set('value', '');
+      view.propertyDidChange('displayValue');
+      expect(view.get('displayValue')).to.be.equal(Em.I18n.t('common.na'));
+    });
+
+    it("value not empty", function() {
+      view.set('value', 'val');
+      view.propertyDidChange('displayValue');
+      expect(view.get('displayValue')).to.be.equal('val');
+    });
+  });
+});
\ No newline at end of file

Reply via email to