Ver código fonte

AMBARI-7789 Unit tests for heatmap metrics page. (atkach)

atkach 10 anos atrás
pai
commit
da27f4124d

+ 2 - 0
ambari-web/app/assets/test/tests.js

@@ -67,6 +67,8 @@ var files = ['test/init_model_test',
   'test/controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceused_test',
   'test/controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused_test',
   'test/controllers/main/charts/heatmap_metrics/heatmap_metric_yarn_ResourceUsed_test',
+  'test/controllers/main/charts/heatmap_metrics/heatmap_metric_processrun_test',
+  'test/controllers/main/charts/heatmap_metrics/heatmap_metric_mapreduce_test',
   'test/controllers/main/host/add_controller_test',
   'test/controllers/main/host/configs_service_test',
   'test/controllers/main/host/details_test',

+ 87 - 65
ambari-web/app/controllers/main/charts/heatmap_metrics/heatmap_metric.js

@@ -115,72 +115,82 @@ App.MainChartHeatmapMetric = Em.Object.extend(heatmap.mappers, {
    * 
    */
   slotDefinitions: function () {
-    var min = this.get('minimumValue');
     var max = parseFloat(this.get('maximumValue'));
     var slotCount = this.get('numberOfSlots');
     var labelSuffix = this.get('slotDefinitionLabelSuffix');
-    var delta = (max - min) / slotCount;
+    var delta = (max - this.get('minimumValue')) / slotCount;
     var defs = [];
     var slotColors = this.get('slotColors');
     var slotColorIndex = 0;
     for ( var c = 0; c < slotCount - 1; c++) {
-      var from = this.formatLegendNumber(c * delta);
-      var to = this.formatLegendNumber((c + 1) * delta);
-      var label;
-      if ($.trim(labelSuffix) == 'ms') {
-      	label = date.timingFormat(from, 'zeroValid') + " - " + date.timingFormat(to, 'zeroValid');
-      } else {
-	      label = from + labelSuffix + " - " + to + labelSuffix;
-      }
       var slotColor = slotColors[slotColorIndex++];
-      defs.push(Em.Object.create({
-        from: from,
-        to: to,
-        label: label,
-        cssStyle: "background-color:rgb(" + slotColor.r + "," + slotColor.g + "," + slotColor.b + ")"
-      }));
+      var start = (c * delta);
+      var end = ((c + 1) * delta);
+      defs.push(this.generateSlot(start, end, labelSuffix, slotColor));
     }
-    from = this.formatLegendNumber((slotCount - 1) * delta);
-    to = this.formatLegendNumber(max);
+    slotColor = slotColors[slotColorIndex++];
+    start = ((slotCount - 1) * delta);
+    defs.push(this.generateSlot(start, max, labelSuffix, slotColor));
 
+    defs.push(Em.Object.create({
+      from: NaN,
+      to: NaN,
+      label: Em.I18n.t('charts.heatmap.label.invalidData'),
+      cssStyle: this.getHatchStyle()
+    }));
+    defs.push(Em.Object.create({
+      from: -1,
+      to: -1,
+      label: Em.I18n.t('charts.heatmap.label.notApplicable'),
+      cssStyle: "background-color:rgb(200, 200, 200)"
+    }));
+    return defs;
+  }.property('minimumValue', 'maximumValue', 'numberOfSlots'),
+  /**
+   * genarate slot by given parameters
+   *
+   * @param min
+   * @param max
+   * @param labelSuffix
+   * @param slotColor
+   * @return {object}
+   * @method generateSlot
+   */
+  generateSlot: function (min, max, labelSuffix, slotColor) {
+    var from = this.formatLegendNumber(min);
+    var to = this.formatLegendNumber(max);
+    var label;
     if ($.trim(labelSuffix) == 'ms') {
       label = date.timingFormat(from, 'zeroValid') + " - " + date.timingFormat(to, 'zeroValid');
     } else {
       label = from + labelSuffix + " - " + to + labelSuffix;
     }
-
-    slotColor = slotColors[slotColorIndex++];
-    defs.push(Em.Object.create({
+    return Em.Object.create({
       from: from,
       to: to,
       label: label,
       cssStyle: "background-color:rgb(" + slotColor.r + "," + slotColor.g + "," + slotColor.b + ")"
-    }));
+    });
+  },
+
+  /**
+   * calculate hatch style of slot according to browser version used
+   * @return {String}
+   */
+  getHatchStyle: function () {
     var hatchStyle = "background-color:rgb(135, 206, 250)";
-    if(jQuery.browser.webkit){
+    if (jQuery.browser.webkit) {
       hatchStyle = "background-image:-webkit-repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
-    }else if(jQuery.browser.mozilla){
+    } else if (jQuery.browser.mozilla) {
       hatchStyle = "background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
-    }else if(jQuery.browser.msie && jQuery.browser.version){
-      var majorVersion =  parseInt(jQuery.browser.version.split('.')[0]);
-      if(majorVersion>9){
+    } else if (jQuery.browser.msie && jQuery.browser.version) {
+      var majorVersion = parseInt(jQuery.browser.version.split('.')[0]);
+      if (majorVersion > 9) {
         hatchStyle = "background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)";
       }
     }
-    defs.push(Em.Object.create({
-      from: NaN,
-      to: NaN,
-      label: Em.I18n.t('charts.heatmap.label.invalidData'),
-      cssStyle: hatchStyle
-    }));
-    defs.push(Em.Object.create({
-      from: -1,
-      to: -1,
-      label: Em.I18n.t('charts.heatmap.label.notApplicable'),
-      cssStyle: "background-color:rgb(200, 200, 200)"
-    }));
-    return defs;
-  }.property('minimumValue', 'maximumValue', 'numberOfSlots'),
+    return hatchStyle;
+  },
 
   /**
    * In slot definitions this value is used to construct the label by appending
@@ -236,41 +246,53 @@ App.MainChartHeatmapMetric = Em.Object.extend(heatmap.mappers, {
 
   hostToValueMap: null,
 
-  hostToSlotMap: function(){
+  hostToSlotMap: function () {
     var hostToValueMap = this.get('hostToValueMap');
-    var slotDefs = this.get('slotDefinitions');
     var hostNames = this.get('hostNames');
     var hostToSlotMap = {};
     if (hostToValueMap && hostNames) {
-      hostNames.forEach(function(hostName){
-        var slot = -1;
-        if (hostName in hostToValueMap) {
-          var value = hostToValueMap[hostName];
-          if (isNaN(value)) {
-            slot = slotDefs.length - 2;
-          } else {
-            for ( var slotIndex = 0; slotIndex < slotDefs.length - 2; slotIndex++) {
-              var slotDef = slotDefs[slotIndex];
-              if (value >= slotDef.from && value <= slotDef.to) {
-                slot = slotIndex;
-              }
-            }
-            if(slot < 0){
-              // Assign it to the last legend
-              slot = slotDefs.length - 3;
-            }
-          }
-        } else {
-          slot = slotDefs.length - 1;
-        }
+      hostNames.forEach(function (hostName) {
+        var slot = this.calculateSlot(hostToValueMap, hostName)
         if (slot > -1) {
           hostToSlotMap[hostName] = slot;
         }
-      });
+      }, this);
     }
     return hostToSlotMap;
   }.property('hostToValueMap', 'slotDefinitions'),
 
+  /**
+   * calculate slot position in hostToSlotMap by hostname
+   * @param hostToValueMap
+   * @param hostName
+   * @return {Number}
+   */
+  calculateSlot: function (hostToValueMap, hostName) {
+    var slot = -1;
+    var slotDefs = this.get('slotDefinitions');
+
+    if (hostName in hostToValueMap) {
+      var value = hostToValueMap[hostName];
+      if (isNaN(value)) {
+        slot = slotDefs.length - 2;
+      } else {
+        for (var slotIndex = 0; slotIndex < slotDefs.length - 2; slotIndex++) {
+          var slotDef = slotDefs[slotIndex];
+          if (value >= slotDef.from && value <= slotDef.to) {
+            slot = slotIndex;
+          }
+        }
+        if (slot < 0) {
+          // Assign it to the last legend
+          slot = slotDefs.length - 3;
+        }
+      }
+    } else {
+      slot = slotDefs.length - 1;
+    }
+    return slot;
+  },
+
   /**
    * Determines which slot each host falls into. This information is given to
    * the callback's #map(hostnameToSlotObject) method. The

+ 7 - 9
ambari-web/app/controllers/main/charts/heatmap_metrics/heatmap_metric_processrun.js

@@ -5,9 +5,9 @@
  * 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
@@ -19,7 +19,7 @@ var App = require('app');
 
 /**
  * Base class for any heatmap metric.
- * 
+ *
  * This class basically provides the following for each heatmap metric.
  * <ul>
  * <li> Provides number of slots in which temperature can fall.
@@ -27,7 +27,7 @@ var App = require('app');
  * <li> Gets JSON data from server and maps response for all hosts into above
  * slots.
  * </ul>
- * 
+ *
  */
 App.MainChartHeatmapProcessRunMetric = App.MainChartHeatmapMetric.extend({
   name: Em.I18n.t('charts.heatmap.metrics.processRun'),
@@ -36,11 +36,9 @@ App.MainChartHeatmapProcessRunMetric = App.MainChartHeatmapMetric.extend({
   units: 'Processes',
   metricMapper: function (json) {
     var map = this._super(json);
-    for ( var host in map) {
-      if (host in map) {
-        var val = map[host];
-        map[host] = val.toFixed(1);
-      }
+    for (var host in map) {
+      var val = map[host];
+      map[host] = val.toFixed(1);
     }
     return map;
   }

+ 37 - 0
ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_mapreduce_test.js

@@ -0,0 +1,37 @@
+/**
+ * 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('controllers/main/charts/heatmap_metrics/heatmap_metric');
+require('controllers/main/charts/heatmap_metrics/heatmap_metric_mapreduce');
+
+describe('App.MainChartHeatmapMapreduceMetrics', function () {
+
+  describe('#metricMapper()', function () {
+    var mainChartHeatmapMapreduceMetrics = App.MainChartHeatmapMapreduceMetrics.create();
+
+    it('launch metricMapperWithTransform() method', function () {
+      sinon.stub(mainChartHeatmapMapreduceMetrics, 'metricMapperWithTransform', Em.K);
+      mainChartHeatmapMapreduceMetrics.set('defaultMetric', 'metric1');
+      mainChartHeatmapMapreduceMetrics.set('transformValue', 'value1');
+
+      mainChartHeatmapMapreduceMetrics.metricMapper({'json': 'json'});
+      expect(mainChartHeatmapMapreduceMetrics.metricMapperWithTransform.calledWith({'json': 'json'}, 'metric1', 'value1')).to.be.true;
+      mainChartHeatmapMapreduceMetrics.metricMapperWithTransform.restore();
+    });
+  });
+});

+ 111 - 0
ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_processrun_test.js

@@ -0,0 +1,111 @@
+/**
+ * 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('controllers/main/charts/heatmap_metrics/heatmap_metric');
+require('controllers/main/charts/heatmap_metrics/heatmap_metric_processrun');
+
+describe('App.MainChartHeatmapProcessRunMetric', function () {
+
+  var tests = [
+    {
+      json: {
+        "items" : [
+          {
+            "Hosts" : {
+              "host_name" : "dev01.hortonworks.com"
+            },
+            "metrics" : {
+              "process" : {
+                "proc_run" : 0.0
+              }
+            }
+          }
+        ]
+      },
+      m: 'One host',
+      result: {'dev01.hortonworks.com': '0.0'}
+    },
+    {
+      json: {
+        "items" : [
+          {
+            "Hosts" : {
+              "host_name" : "dev01.hortonworks.com"
+            },
+            "metrics" : {
+              "process" : {
+                "proc_run" : 0.1
+              }
+            }
+          },
+          {
+            "Hosts" : {
+              "host_name" : "dev02.hortonworks.com"
+            },
+            "metrics" : {
+              "process" : {
+                "proc_run" : 0.46
+              }
+            }
+          }
+        ]
+      },
+      m: 'Two hosts',
+      result: {'dev01.hortonworks.com': '0.1', 'dev02.hortonworks.com': '0.5'}
+    },
+    {
+      json: {
+        "items" : [
+          {
+            "Hosts" : {
+              "host_name" : "dev01.hortonworks.com"
+            },
+            "metrics" : {
+              "process" : {
+                "proc_run" : 0.99
+              }
+            }
+          },
+          {
+            "Hosts" : {
+              "host_name" : "dev02.hortonworks.com"
+            },
+            "metrics" : {
+              "process" : {
+              }
+            }
+          }
+        ]
+      },
+      m: 'Two hosts, One without metric',
+      result: {'dev01.hortonworks.com': '1.0'}
+    }
+  ];
+
+  describe('#metricMapper()', function() {
+    var mainChartHeatmapProcessRunMetric = App.MainChartHeatmapProcessRunMetric.create();
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        var r = mainChartHeatmapProcessRunMetric.metricMapper(test.json);
+        expect(r).to.eql(test.result);
+      });
+    });
+  });
+});

+ 272 - 19
ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js

@@ -17,11 +17,15 @@
 
 var App = require('app');
 require('controllers/main/charts/heatmap_metrics/heatmap_metric');
+var date = require('utils/date');
 
 describe('MainChartHeatmapMetric', function () {
-
   var mainChartHeatmapMetric = App.MainChartHeatmapMetric.create({});
 
+  beforeEach(function () {
+    mainChartHeatmapMetric = App.MainChartHeatmapMetric.create({});
+  });
+
   describe('#formatLegendNumber', function () {
     var tests = [
       {m:'undefined to undefined',i:undefined,e:undefined},
@@ -41,33 +45,282 @@ describe('MainChartHeatmapMetric', function () {
 
   describe('#refreshHostSlots', function() {
     beforeEach(function() {
-      App.set('apiPrefix', '/api/v1');
-      App.set('clusterName', 'tdk');
-      sinon.stub(App, 'get', function(k) {
-        if ('testMode' === k) return false;
-        return Em.get(App, k);
-      });
-      sinon.spy($, 'ajax');
+      sinon.stub(App.ajax, 'send', Em.K);
     });
 
     afterEach(function() {
-      $.ajax.restore();
-      App.get.restore();
-    });
-
-    mainChartHeatmapMetric  = App.MainChartHeatmapMetric.create({});
-    mainChartHeatmapMetric.set('ajaxIndex', 'hosts.metrics.host_component');
-    mainChartHeatmapMetric.set('ajaxData', {
-      serviceName: 'SERVICE',
-      componentName: 'COMPONENT'
+      App.ajax.send.restore();
     });
-    mainChartHeatmapMetric.set('defaultMetric', 'default.metric');
 
     it('Should load proper URL', function() {
+      mainChartHeatmapMetric.set('ajaxData', {
+        serviceName: 'SERVICE',
+        componentName: 'COMPONENT'
+      });
+      mainChartHeatmapMetric.set('defaultMetric', 'default.metric');
       mainChartHeatmapMetric.refreshHostSlots();
-      expect($.ajax.args[0][0].url.endsWith('/api/v1/clusters/tdk/services/SERVICE/components/COMPONENT?fields=host_components/default/metric')).to.equal(true);
+      expect(App.ajax.send.getCall(0).args[0].data).to.eql({
+        "metricName": "default/metric",
+        "serviceName": "SERVICE",
+        "componentName": "COMPONENT"
+      });
+      expect(App.ajax.send.getCall(0).args[0].name).to.equal('hosts.metrics');
+    });
+  });
+
+  describe('#slotDefinitions', function () {
+    beforeEach(function () {
+      sinon.stub(mainChartHeatmapMetric, 'generateSlot', Em.K);
+    });
+    afterEach(function () {
+      mainChartHeatmapMetric.generateSlot.restore();
+    });
+    it('one slot', function () {
+      mainChartHeatmapMetric.set('numberOfSlots', 1);
+      mainChartHeatmapMetric.set('maximumValue', 100);
+      mainChartHeatmapMetric.set('minimumValue', 0);
+
+      mainChartHeatmapMetric.propertyDidChange('slotDefinitions');
+
+      expect(mainChartHeatmapMetric.get('slotDefinitions').length).to.equal(3);
+      expect(mainChartHeatmapMetric.generateSlot.getCall(0).args).to.eql([0, 100, '', {r: 0, g: 204, b: 0}]);
+      expect(mainChartHeatmapMetric.generateSlot.callCount).to.be.equal(1);
+    });
+    it('two slots', function () {
+      mainChartHeatmapMetric.set('numberOfSlots', 2);
+      mainChartHeatmapMetric.set('maximumValue', 100);
+      mainChartHeatmapMetric.set('minimumValue', 0);
+
+      mainChartHeatmapMetric.propertyDidChange('slotDefinitions');
+
+      expect(mainChartHeatmapMetric.get('slotDefinitions').length).to.equal(4);
+      expect(mainChartHeatmapMetric.generateSlot.getCall(0).args).to.eql([0, 50, '', {r: 0, g: 204, b: 0}]);
+      expect(mainChartHeatmapMetric.generateSlot.getCall(1).args).to.eql([50, 100, '', {r: 159, g: 238, b: 0}]);
+      expect(mainChartHeatmapMetric.generateSlot.callCount).to.be.equal(2);
+    });
+  });
+
+  describe('#generateSlot()', function () {
+    beforeEach(function () {
+      sinon.stub(mainChartHeatmapMetric, 'formatLegendNumber').returns('val');
+      sinon.stub(date, 'timingFormat').returns('time');
+    });
+    afterEach(function () {
+      mainChartHeatmapMetric.formatLegendNumber.restore();
+      date.timingFormat.restore();
+    });
+    it('label suffix is empty', function () {
+      expect(mainChartHeatmapMetric.generateSlot(0, 1, '', {r: 0, g: 0, b: 0})).to.eql(Em.Object.create({
+        "from": "val",
+        "to": "val",
+        "label": "val - val",
+        "cssStyle": "background-color:rgb(0,0,0)"
+      }));
+
+      expect(mainChartHeatmapMetric.formatLegendNumber.getCall(0).args).to.eql([0]);
+      expect(mainChartHeatmapMetric.formatLegendNumber.getCall(1).args).to.eql([1]);
     });
+    it('label suffix is "ms"', function () {
+      expect(mainChartHeatmapMetric.generateSlot(0, 1, 'ms', {r: 0, g: 0, b: 0})).to.eql(Em.Object.create({
+        "from": "val",
+        "to": "val",
+        "label": "time - time",
+        "cssStyle": "background-color:rgb(0,0,0)"
+      }));
 
+      expect(mainChartHeatmapMetric.formatLegendNumber.getCall(0).args).to.eql([0]);
+      expect(mainChartHeatmapMetric.formatLegendNumber.getCall(1).args).to.eql([1]);
+      expect(date.timingFormat.getCall(0).args).to.eql(['val', 'zeroValid']);
+      expect(date.timingFormat.getCall(1).args).to.eql(['val', 'zeroValid']);
+    });
+  });
+
+  describe('#getHatchStyle()', function () {
+    var testCases = [
+      {
+        title: 'unknown browser',
+        data: {},
+        result: 'background-color:rgb(135, 206, 250)'
+      },
+      {
+        title: 'webkit browser',
+        data: {
+          webkit: true
+        },
+        result: 'background-image:-webkit-repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)'
+      },
+      {
+        title: 'mozilla browser',
+        data: {
+          mozilla: true
+        },
+        result: 'background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)'
+      },
+      {
+        title: 'IE version 9',
+        data: {
+          msie: true,
+          version: '9.0'
+        },
+        result: 'background-color:rgb(135, 206, 250)'
+      },
+      {
+        title: 'IE version 10',
+        data: {
+          msie: true,
+          version: '10.0'
+        },
+        result: 'background-image:repeating-linear-gradient(-45deg, #FF1E10, #FF1E10 3px, #ff6c00 3px, #ff6c00 6px)'
+      }
+    ]
+
+    testCases.forEach(function(test){
+      it(test.title, function () {
+        jQuery.browser = test.data;
+        expect(mainChartHeatmapMetric.getHatchStyle()).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#hostToSlotMap', function () {
+    it('hostToValueMap is null', function () {
+      mainChartHeatmapMetric.set('hostToValueMap', null);
+      mainChartHeatmapMetric.set('hostNames', []);
+      mainChartHeatmapMetric.propertyDidChange('hostToSlotMap');
+      expect(mainChartHeatmapMetric.get('hostToSlotMap')).to.be.empty;
+    });
+    it('hostNames is null', function () {
+      mainChartHeatmapMetric.set('hostToValueMap', {});
+      mainChartHeatmapMetric.set('hostNames', null);
+      mainChartHeatmapMetric.propertyDidChange('hostToSlotMap');
+      expect(mainChartHeatmapMetric.get('hostToSlotMap')).to.be.empty;
+    });
+    it('slot greater than -1', function () {
+      mainChartHeatmapMetric.set('hostToValueMap', {});
+      mainChartHeatmapMetric.set('hostNames', ['host1']);
+      sinon.stub(mainChartHeatmapMetric, 'calculateSlot').returns(0);
+      mainChartHeatmapMetric.propertyDidChange('hostToSlotMap');
+      expect(mainChartHeatmapMetric.get('hostToSlotMap')).to.eql({'host1': 0});
+      expect(mainChartHeatmapMetric.calculateSlot.calledWith({}, 'host1')).to.be.true;
+      mainChartHeatmapMetric.calculateSlot.restore();
+    });
+    it('slot equal to -1', function () {
+      mainChartHeatmapMetric.set('hostToValueMap', {});
+      mainChartHeatmapMetric.set('hostNames', ['host1']);
+      sinon.stub(mainChartHeatmapMetric, 'calculateSlot').returns('-1');
+      mainChartHeatmapMetric.propertyDidChange('hostToSlotMap');
+      expect(mainChartHeatmapMetric.get('hostToSlotMap')).to.be.empty;
+      expect(mainChartHeatmapMetric.calculateSlot.calledWith({}, 'host1')).to.be.true;
+      mainChartHeatmapMetric.calculateSlot.restore();
+    });
+  });
+
+  describe('#calculateSlot()', function () {
+    var testCases = [
+      {
+        title: 'hostToValueMap is empty',
+        data: {
+          hostToValueMap: {},
+          hostName: 'host1',
+          slotDefinitions: []
+        },
+        result: -1
+      },
+      {
+        title: 'host value is NaN',
+        data: {
+          hostToValueMap: {'host1': NaN},
+          hostName: 'host1',
+          slotDefinitions: []
+        },
+        result: -2
+      },
+      {
+        title: 'host value correct but slotDefinitions does not contain host value',
+        data: {
+          hostToValueMap: {'host1': 1},
+          hostName: 'host1',
+          slotDefinitions: [{}, {}]
+        },
+        result: -1
+      },
+      {
+        title: 'host value -1',
+        data: {
+          hostToValueMap: {'host1': -1},
+          hostName: 'host1',
+          slotDefinitions: [
+            {
+              from: 0,
+              to: 10
+            },
+            {},
+            {}
+          ]
+        },
+        result: 0
+      },
+      {
+        title: 'host value 11',
+        data: {
+          hostToValueMap: {'host1': 11},
+          hostName: 'host1',
+          slotDefinitions: [
+            {
+              from: 0,
+              to: 10
+            },
+            {},
+            {}
+          ]
+        },
+        result: 0
+      },
+      {
+        title: 'host value 5',
+        data: {
+          hostToValueMap: {'host1': 5},
+          hostName: 'host1',
+          slotDefinitions: [
+            {},
+            {
+              from: 0,
+              to: 10
+            },
+            {},
+            {}
+          ]
+        },
+        result: 1
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        sinon.stub(mainChartHeatmapMetric, 'get').withArgs('slotDefinitions').returns(test.data.slotDefinitions);
+        expect(mainChartHeatmapMetric.calculateSlot(test.data.hostToValueMap, test.data.hostName)).to.equal(test.result);
+        mainChartHeatmapMetric.get.restore();
+      });
+    });
+  });
+
+  describe('#refreshHostSlotsSuccessCallback()', function () {
+    it('launch metricMapper with recieved data', function () {
+      sinon.stub(mainChartHeatmapMetric, 'metricMapper').returns({'host1': 1});
+      mainChartHeatmapMetric.refreshHostSlotsSuccessCallback({data: 'data'});
+      expect(mainChartHeatmapMetric.get('hostToValueMap')).to.eql({'host1': 1});
+      expect(mainChartHeatmapMetric.get('loading')).to.be.false;
+      expect(mainChartHeatmapMetric.metricMapper.calledWith({data: 'data'})).to.be.true;
+      mainChartHeatmapMetric.metricMapper.restore();
+    });
+  });
+
+  describe('#refreshHostSlotsErrorCallback()', function () {
+    it('', function () {
+      mainChartHeatmapMetric.set('loading', undefined);
+      mainChartHeatmapMetric.refreshHostSlotsErrorCallback({data: 'data'});
+      expect(mainChartHeatmapMetric.get('loading')).to.be.false;
+    });
   });
 
 });

+ 12 - 9
ambari-web/test/controllers/main/host/details_test.js

@@ -22,6 +22,7 @@ require('controllers/main/host/details');
 require('models/service');
 require('models/host_component');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 var controller;
 
 describe('App.MainHostDetailsController', function () {
@@ -1464,23 +1465,25 @@ describe('App.MainHostDetailsController', function () {
   describe('#downloadClientConfigs()', function () {
 
     beforeEach(function () {
-      sinon.stub($, 'fileDownload', function() {
-        return {
-          fail: function() { return false; }
-        }
-      });
+      sinon.stub(componentsUtils, 'downloadClientConfigs', Em.K);
     });
     afterEach(function () {
-      $.fileDownload.restore();
+      componentsUtils.downloadClientConfigs.restore();
     });
 
-    it('should launch $.fileDownload method', function () {
+    it('should launch componentsUtils.downloadClientConfigs method', function () {
       controller.downloadClientConfigs({
         context: Em.Object.create({
-          componentName: 'name'
+          componentName: 'name',
+          hostName: 'host1',
+          displayName: 'dName'
         })
       });
-      expect($.fileDownload.calledOnce).to.be.true;
+      expect(componentsUtils.downloadClientConfigs.calledWith({
+        componentName: 'name',
+        hostName: 'host1',
+        displayName: 'dName'
+      })).to.be.true;
     });
   });
 

+ 25 - 9
ambari-web/test/controllers/main/service/item_test.js

@@ -27,6 +27,7 @@ require('controllers/global/cluster_controller');
 require('controllers/main/service/reassign_controller');
 require('controllers/main/service/item');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 
 describe('App.MainServiceItemController', function () {
 
@@ -602,26 +603,41 @@ describe('App.MainServiceItemController', function () {
       content: {
         hostComponents: [
           Em.Object.create({
-            isClient: true
+            isClient: true,
+            componentName: 'C1',
+            displayName: 'd1'
           })
-        ]
+        ],
+        serviceName: 'S1'
       }
     });
 
     beforeEach(function () {
-      sinon.stub($, 'fileDownload', function() {
-        return {
-          fail: function() { return false; }
-        }
-      });
+      sinon.stub(componentsUtils, 'downloadClientConfigs', Em.K);
     });
     afterEach(function () {
-      $.fileDownload.restore();
+      componentsUtils.downloadClientConfigs.restore();
     });
 
     it('should launch $.fileDownload method', function () {
       mainServiceItemController.downloadClientConfigs();
-      expect($.fileDownload.calledOnce).to.be.true;
+      expect(componentsUtils.downloadClientConfigs.calledWith({
+        serviceName: 'S1',
+        componentName: 'C1',
+        displayName: 'd1'
+      })).to.be.true;
+    });
+    it('should launch $.fileDownload method, event passed', function () {
+      var event = {
+        label: 'label1',
+        name: 'name1'
+      };
+      mainServiceItemController.downloadClientConfigs(event);
+      expect(componentsUtils.downloadClientConfigs.calledWith({
+        serviceName: 'S1',
+        componentName: 'name1',
+        displayName: 'label1'
+      })).to.be.true;
     });
   });