You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2013/08/08 20:27:13 UTC

[2/3] AMBARI-2829. Dashboard refactor and unit tests. (onechiporenko via yusaku)

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js b/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
index 80029fc..7d283a7 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
+++ b/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
@@ -19,98 +19,38 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.NameNodeHeapPieChartView = App.DashboardWidgetView.extend({
+App.NameNodeHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.NameNodeHeap'),
   id: '1',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'hdfs',
 
-  hiddenInfo: function () {
-  var memUsed = this.get('model').get('jvmMemoryHeapUsed');
-  var memCommitted = this.get('model').get('jvmMemoryHeapCommitted');
-  var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
-  var result = [];
-  result.pushObject(percent.toFixed(1) + '% used');
-  result.pushObject(numberUtils.bytesToSize(memUsed, 1, 'parseFloat', 1000000) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, 'parseFloat', 1000000));
-  return result;
-  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
-
-  thresh1: null,
-  thresh2: null,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.jvmMemoryHeapCommitted') * 1000000;
-    return total > 0 ;
-  }.property('model.jvmMemoryHeapCommitted'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-nn-heap', // html id
-    stroke: '#D6DDDF', //light grey
-    thresh1: null, //bind from parent
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var used = this.get('model.jvmMemoryHeapUsed') * 1000000;
-      var total = this.get('model.jvmMemoryHeapCommitted') * 1000000;
-      var percent = total > 0 ? ((used)*100 / total).toFixed() : 0;
-      return [ percent, 100 - percent];
-    }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
-
-    contentColor: function () {
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
-
-    // refresh text and color when data in model changed
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      if(old_svg){
-        old_svg.remove();
-      }
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
-
-})
-
-
+ /* hiddenInfo: function () {
+    var memUsed = this.get('model').get('jvmMemoryHeapUsed');
+    var memCommitted = this.get('model').get('jvmMemoryHeapCommitted');
+    var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
+    var result = [];
+    result.pushObject(percent.toFixed(1) + '% used');
+    result.pushObject(numberUtils.bytesToSize(memUsed, 1, 'parseFloat', 1024 * 1024) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, 'parseFloat', 1024 * 1024));
+    return result;
+  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),*/
+
+  modelFieldMax: 'jvmMemoryHeapCommitted',
+  modelFieldUsed: 'jvmMemoryHeapUsed',
+
+  widgetHtmlId: 'widget-nn-heap',
+
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  },
+
+  getUsed: function() {
+    return this.get('model').get(this.get('modelFieldUsed')) * 1024 * 1024 || 0;
+  },
+
+  getMax: function() {
+    return this.get('model').get(this.get('modelFieldMax')) * 1024 * 1024 || 0;
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js b/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
index e994439..749d5d3 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
+++ b/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.NameNodeRpcView = App.DashboardWidgetView.extend({
+App.NameNodeRpcView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.NameNodeRpc'),
   id: '5',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'hdfs',
   hiddenInfo: function () {
     var result = [];
@@ -35,26 +31,6 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
     return result;
   }.property('model.nameNodeRpc'),
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-  isNA: function () {
-    return this.get('data') === null;
-  }.property('data'),
-
   thresh1: 0.5,
   thresh2: 2,
   maxValue: 'infinity',
@@ -74,8 +50,9 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
   content: function () {
     if (this.get('data') || this.get('data') == 0) {
       return this.get('data') + " ms";
-    } else {
-      return this.t('services.service.summary.notAvailable');
+    }
+    else {
+      return Em.I18n.t('services.service.summary.notAvailable');
     }
   }.property('model.nameNodeRpc'),
 
@@ -84,9 +61,7 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the thresholds to change the color of current widget. ' +
-        ' The unit is milli-second. '+
-        ' So enter two numbers larger than 0. ',
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint3'),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -129,7 +104,7 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: ['sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -153,10 +128,6 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var colors = ['#95A800', '#FF8E00', '#B80000']; //color green, orange ,red
@@ -172,28 +143,9 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
             max: 100,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             }
           });
-
-          function updateColors (handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', true);
@@ -202,4 +154,4 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
     });
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js b/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
index 216ef4c..60ad42d 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
+++ b/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
@@ -17,107 +17,20 @@
  */
 
 var App = require('app');
-var date = require('utils/date');
 
-App.NameNodeUptimeView = App.DashboardWidgetView.extend({
+App.NameNodeUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/uptime'),
   title: Em.I18n.t('dashboard.widgets.NameNodeUptime'),
   id: '15',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'hdfs',
-  hiddenInfo: [],
-  hiddenInfoClass: "hidden-info-three-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    return this.get('data') != null;
-  }.property('data'),
-  isOrange: function () {
-    return false;
-  }.property('data'),
-  isRed: function () {
-    return false;
-  }.property('data'),
-  isNA: function () {
-    return this.get('data') == null;
-  }.property('data'),
+  component: 'NameNode',
+  modelField: 'nameNodeStartTime',
 
-  thresh1: 5,
-  thresh2: 10,
-  maxValue: 'infinity',
-
-  data: function () {
-    var uptime = this.get('model.nameNodeStartTime');
-    if (uptime && uptime > 0) {
-      var uptimeString = this.timeConverter(uptime);
-      var diff = (new Date()).getTime() - uptime;
-      if (diff < 0) {
-        diff = 0;
-      }
-      var formatted = date.timingFormat(diff); //17.67 days
-      var timeUnit = null;
-      switch (formatted.split(" ")[1]) {
-        case 'secs':
-          timeUnit = 's';
-          break;
-        case 'hours':
-          timeUnit = 'hr';
-          break;
-        case 'days':
-          timeUnit = 'd';
-          break;
-        case 'mins':
-          timeUnit = 'min';
-          break;
-        default:
-          timeUnit = formatted.split(" ")[1];
-      }
-      this.set('timeUnit', timeUnit);
-      this.set('hiddenInfo', []);
-      this.get('hiddenInfo').pushObject(formatted);
-      this.get('hiddenInfo').pushObject(uptimeString[0]);
-      this.get('hiddenInfo').pushObject(uptimeString[1]);
-      return parseFloat(formatted.split(" ")[0]);
-    }
-    this.set('hiddenInfo', []);
-    this.set('hiddenInfo', ['NameNode','Not Running']);
-    return null;
-  }.property('model.nameNodeStartTime'),
-
-  timeUnit: null,
-
-  content: function () {
-    var data = this.get('data');
-    if (data) {
-      return data.toFixed(1) + ' '+ this.get('timeUnit');
-    } else {
-      return this.t('services.service.summary.notAvailable');
-    }
-  }.property('model.nameNodeStartTime'),
-
-  timeConverter: function (timestamp){
-    var origin = new Date(timestamp);
-    origin = origin.toString();
-    var result = [];
-    var start = origin.indexOf('GMT');
-    if (start == -1) { // ie
-      var arr = origin.split(" ");
-      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
-      var second = '';
-      for (var i = 4; i < arr.length; i++) {
-        second = second + " " + arr[i];
-      }
-      result.pushObject(second);
-    } else { // other browsers
-      var end = origin.indexOf(" ", start);
-      result.pushObject(origin.slice(0, start-10));
-      result.pushObject(origin.slice(start-9));
-    }
-    return result;
+  didInsertElement: function() {
+    this._super();
+    this.calc();
   }
 
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js b/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
index f94d28a..55d02e0 100644
--- a/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
+++ b/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.NodeManagersLiveView = App.DashboardWidgetView.extend({
+App.NodeManagersLiveView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.NodeManagersLive'),
   id: '26',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'yarn',
 
   hiddenInfo: function () {
@@ -46,23 +42,6 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
     'model.nodeManagersCountUnhealthy', 'model.nodeManagersCountRebooted', 'model.nodeManagersCountDecommissioned'),
   hiddenInfoClass: "hidden-info-five-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen'],
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-
   thresh1: 40,
   thresh2: 70,
   maxValue: 100,
@@ -85,9 +64,7 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the percentage of thresholds to change the color of current widget. ' +
-        ' Assume all task trackers UP is 100, and all DOWN is 0. '+
-        ' So enter two numbers between 0 to ' + max_tmp,
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint1').format(max_tmp),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -158,12 +135,9 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
+        var self = this;
         var handlers = [configObj.get('thresh1'), configObj.get('thresh2')];
         var colors = ['#B80000', '#FF8E00', '#95A800']; //color red, orange, green
 
@@ -176,36 +150,17 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
             max: max_tmp,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers,colors);
             },
             slide: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values,colors);
               configObj.set('thresh1', ui.values[0] + '');
               configObj.set('thresh2', ui.values[1] + '');
             },
             change: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values,colors);
             }
           });
-
-          function updateColors(handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', false);

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js b/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
new file mode 100644
index 0000000..502ea69
--- /dev/null
+++ b/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
@@ -0,0 +1,140 @@
+/**
+ * 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');
+var numberUtils = require('utils/number_utils');
+
+App.PieChartDashboardWidgetView = App.DashboardWidgetView.extend({
+
+  templateName: require('templates/main/dashboard/widgets/pie_chart'),
+
+  maxValue: 100,
+
+  modelFieldMax: null,
+  modelFieldUsed: null,
+
+  hiddenInfo: null,
+
+  isPieExist: null,
+
+  dataForPieChart: null,
+
+  widgetHtmlId: null,
+
+  getUsed: function() {
+    return this.get('model').get(this.get('modelFieldUsed')) || 0;
+  },
+
+  getMax: function() {
+    return this.get('model').get(this.get('modelFieldMax')) || 0;
+  },
+
+  calcHiddenInfo: function() {
+    var used = this.getUsed();
+    var max = this.getMax();
+    var percent = max > 0 ? 100 * used / max : 0;
+    var result = [];
+    result.pushObject(percent.toFixed(1) + '% used');
+    result.pushObject(numberUtils.bytesToSize(used, 1, 'parseFloat', 1024 * 1024) + ' of ' + numberUtils.bytesToSize(max, 1, 'parseFloat', 1024 * 1024));
+    return result;
+  },
+
+  calcIsPieExists: function() {
+    return (this.get('model').get(this.get('modelFieldMax')) > 0);
+  },
+
+  calcDataForPieChart: function() {
+    var used = this.get('model').get(this.get('modelFieldUsed'));
+    var total = this.get('model').get(this.get('modelFieldMax'));
+    var percent = total > 0 ? ((used)*100 / total).toFixed() : 0;
+    return [ percent, 100 - percent];
+  },
+
+  calc: function() {
+    this.set('hiddenInfo', this.calcHiddenInfo());
+    this.set('isPieExist', this.calcIsPieExists());
+    this.set('dataForPieChart', this.calcDataForPieChart());
+  },
+
+  didInsertElement: function() {
+    this._super();
+    this.addObserver('model.' + this.get('modelFieldMax'), this, this.calc);
+    this.addObserver('model.' + this.get('modelFieldUsed'), this, this.calc);
+  },
+
+  content: App.ChartPieView.extend({
+    model: null,  //data bind here
+    id: function() {
+      return this.get('parentView.widgetHtmlId');
+    }.property('parentView.widgetHtmlId'), // html id
+    stroke: '#D6DDDF', //light grey
+    thresh1: null, //bind from parent
+    thresh2: null,
+    innerR: 25,
+
+    existCenterText: true,
+    centerTextColor: function () {
+      return this.get('contentColor');
+    }.property('contentColor'),
+
+    palette: new Rickshaw.Color.Palette({
+      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
+    }),
+
+    data: function() {
+      return this.get('parentView.dataForPieChart');
+    }.property('parentView.dataForPieChart'),
+
+    contentColor: function () {
+      var used = parseFloat(this.get('data')[0]);
+      var thresh1 = parseFloat(this.get('thresh1'));
+      var thresh2 = parseFloat(this.get('thresh2'));
+      var color_green = '#95A800';
+      var color_red = '#B80000';
+      var color_orange = '#FF8E00';
+      if (used <= thresh1) {
+        this.set('palette', new Rickshaw.Color.Palette({
+          scheme: [ '#FFFFFF', color_green  ].reverse()
+        }));
+        return color_green;
+      } else if (used <= thresh2) {
+        this.set('palette', new Rickshaw.Color.Palette({
+          scheme: [ '#FFFFFF', color_orange  ].reverse()
+        }));
+        return color_orange;
+      } else {
+        this.set('palette', new Rickshaw.Color.Palette({
+          scheme: [ '#FFFFFF', color_red  ].reverse()
+        }));
+        return color_red;
+      }
+    }.property('data', 'thresh1', 'thresh2'),
+
+    // refresh text and color when data in model changed
+    refreshSvg: function () {
+      // remove old svg
+      var old_svg =  $("#" + this.get('id'));
+      if(old_svg){
+        old_svg.remove();
+      }
+
+      // draw new svg
+      this.appendSvg();
+    }.observes('data', 'thresh1', 'thresh2')
+  })
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js b/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
index f274fc7..966de98 100644
--- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
+++ b/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
@@ -19,18 +19,13 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.ResourceManagerHeapPieChartView = App.DashboardWidgetView.extend({
+App.ResourceManagerHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.ResourceManagerHeap'),
   id: '24',
-
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'yarn',
 
-  hiddenInfo: function () {
+  /*hiddenInfo: function () {
     var memUsed = this.get('model').get('jvmMemoryHeapUsed');
     var memCommitted = this.get('model').get('jvmMemoryHeapCommitted');
     var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
@@ -38,75 +33,16 @@ App.ResourceManagerHeapPieChartView = App.DashboardWidgetView.extend({
     result.pushObject(percent.toFixed(1) + '% used');
     result.pushObject(numberUtils.bytesToSize(memUsed, 1, "parseFloat", 1000000) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, "parseFloat", 1000000));
     return result;
-  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
-
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.jvmMemoryHeapCommitted') * 1000000;
-    return total > 0 ;
-  }.property('model.jvmMemoryHeapCommitted'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-rm-heap', // id in html
-    stroke: '#D6DDDF', //light grey
-    thresh1: null,
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var memUsed = this.get('model').get('jvmMemoryHeapUsed') * 1000000;
-      var memCommitted = this.get('model').get('jvmMemoryHeapCommitted') * 1000000;
-      var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted).toFixed() : 0;
-      return [ percent, 100 - percent];
-    }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
+  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),*/
 
-    contentColor: function (){
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
+  modelFieldMax: 'jvmMemoryHeapCommitted',
+  modelFieldUsed: 'jvmMemoryHeapUsed',
 
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
+  widgetHtmlId: 'widget-rm-heap',
 
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
 
-})
\ No newline at end of file
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
index d3b092d..cb7ab84 100644
--- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
+++ b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
@@ -19,104 +19,19 @@
 var App = require('app');
 var date = require('utils/date');
 
-App.ResourceManagerUptimeView = App.DashboardWidgetView.extend({
+App.ResourceManagerUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/uptime'),
   title: Em.I18n.t('dashboard.widgets.ResourceManagerUptime'),
   id: '25',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'yarn',
-  hiddenInfo: [],
-  hiddenInfoClass: "hidden-info-three-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    return this.get('data') != null;
-  }.property('data'),
-  isOrange: function () {
-    return false;
-  }.property('data'),
-  isRed: function () {
-    return false;
-  }.property('data'),
-  isNA: function () {
-    return this.get('data') == null;
-  }.property('data'),
+  component: 'ResourceManager',
+  modelField: 'resourceManagerStartTime',
 
-  thresh1: 5,
-  thresh2: 10,
-  maxValue: 'infinity',
-
-  data: function(){
-    var uptime = this.get('model.resourceManagerStartTime');
-    if (uptime && uptime > 0) {
-      var uptimeString = this.timeConverter(uptime);
-      var diff = (new Date()).getTime() - uptime;
-      if (diff < 0) {
-        diff = 0;
-      }
-      var formatted = date.timingFormat(diff); //17.67 days
-      var timeUnit = null;
-      switch (formatted.split(" ")[1]){
-        case 'secs':
-          timeUnit = 's';
-          break;
-        case 'hours':
-          timeUnit = 'hr';
-          break;
-        case 'days':
-          timeUnit = 'd';
-          break;
-        case 'mins':
-          timeUnit = 'min';
-          break;
-        default:
-          timeUnit = formatted.split(" ")[1];
-      }
-      this.set('timeUnit', timeUnit);
-      this.set('hiddenInfo', []);
-      this.get('hiddenInfo').pushObject(formatted);
-      this.get('hiddenInfo').pushObject(uptimeString[0]);
-      this.get('hiddenInfo').pushObject(uptimeString[1]);
-      return parseFloat(formatted.split(" ")[0]);
-    }
-    this.set('hiddenInfo', ['ResourceManager','Not Running']);
-    return null;
-  }.property('model.resourceManagerStartTime'),
-
-  timeUnit: null,
-
-  content: function () {
-    var data = this.get('data');
-    if (data) {
-      return data.toFixed(1) + ' '+ this.get('timeUnit');
-    } else {
-      return this.t('services.service.summary.notAvailable');
-    }
-  }.property('model.jobTrackerStartTime'),
-
-  timeConverter: function (timestamp) {
-    var origin = new Date(timestamp);
-    origin = origin.toString();
-    var result = [];
-    var start = origin.indexOf('GMT');
-    if(start == -1){ // ie
-      var arr = origin.split(" ");
-      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
-      var second = '';
-      for(var i = 4; i < arr.length; i++){
-        second = second + " " + arr[i];
-      }
-      result.pushObject(second);
-    }else{ // other browsers
-      var end = origin.indexOf(" ", start);
-      result.pushObject(origin.slice(0, start-10));
-      result.pushObject(origin.slice(start-9));
-    }
-    return result;
+  didInsertElement: function() {
+    this._super();
+    this.calc();
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js b/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
index 77469c1..b7cd038 100644
--- a/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
+++ b/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.TaskTrackerUpView = App.DashboardWidgetView.extend({
+App.TaskTrackerUpView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.TaskTrackerUp'),
   id: '8',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'mapreduce',
 
   hiddenInfo: function () {
@@ -39,23 +35,6 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
     return result;
   }.property('model.aliveTrackers.length', 'model.taskTrackers.length'),
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen'],
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-
   thresh1: 40,
   thresh2: 70,
   maxValue: 100,
@@ -74,9 +53,7 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the percentage of thresholds to change the color of current widget. ' +
-        ' Assume all task trackers UP is 100, and all DOWN is 0. '+
-        ' So enter two numbers between 0 to ' + max_tmp,
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint1').format(max_tmp),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -124,7 +101,7 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: [ 'sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -147,10 +124,6 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var handlers = [configObj.get('thresh1'), configObj.get('thresh2')];
@@ -165,36 +138,19 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
             max: max_tmp,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             },
             slide: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values, colors);
               configObj.set('thresh1', ui.values[0] + '');
               configObj.set('thresh2', ui.values[1] + '');
             },
             change: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values, colors);
             }
           });
 
-          function updateColors(handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
+
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', false);
@@ -203,4 +159,4 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
     });
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/text_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/text_widget.js b/ambari-web/app/views/main/dashboard/widgets/text_widget.js
new file mode 100644
index 0000000..85b2d54
--- /dev/null
+++ b/ambari-web/app/views/main/dashboard/widgets/text_widget.js
@@ -0,0 +1,66 @@
+/**
+ * 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');
+var date = require('utils/date');
+
+App.TextDashboardWidgetView = App.DashboardWidgetView.extend({
+
+  templateName: require('templates/main/dashboard/widgets/simple_text'),
+
+  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
+
+  isRed: function () {
+    return (this.get('data') <= this.get('thresh1'));
+  }.property('data','thresh1'),
+
+  isOrange: function () {
+    return (this.get('data') <= this.get('thresh2') && this.get('data') > this.get('thresh1'));
+  }.property('data','thresh1','thresh2'),
+
+  isGreen: function () {
+    return (this.get('data') > this.get('thresh2'));
+  }.property('data','thresh2'),
+
+  isNA: function () {
+    return this.get('data') === null;
+  }.property('data'),
+
+  hiddenInfo: [],
+  thresh1: null,
+  thresh2: null,
+  maxValue: null,
+  updateColors: function(handlers, colors) {
+    var colorstops = colors[0] + ", "; // start with the first color
+    for (var i = 0; i < handlers.length; i++) {
+      colorstops += colors[i] + " " + handlers[i] + "%,";
+      colorstops += colors[i+1] + " " + handlers[i] + "%,";
+    }
+    colorstops += colors[colors.length - 1];
+    var cssForChromeAndSafari = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
+    var slider = $('#slider-range');
+    slider.css('background-image', cssForChromeAndSafari);
+    var cssForIE = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
+    slider.css('background-image', cssForIE);
+    //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
+    var cssForFireFox = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
+    slider.css('background-image', cssForFireFox);
+
+    slider.find('.ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
new file mode 100644
index 0000000..251ef2d
--- /dev/null
+++ b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
@@ -0,0 +1,149 @@
+/**
+ * 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');
+var date = require('utils/date');
+
+App.UptimeTextDashboardWidgetView = App.TextDashboardWidgetView.extend({
+
+  templateName: require('templates/main/dashboard/widgets/uptime'),
+
+  timeUnit: null,
+
+  hiddenInfoClass: "hidden-info-three-line",
+
+  thresh1: 5,
+  thresh2: 10,
+  maxValue: 'infinity',
+
+  component: null,
+  /**
+   * Model's field that used to calculate data and content
+   * Should be defined in every child
+   */
+  modelField: null,
+
+  data: null,
+  content: null,
+
+  isGreen: function () {
+    return this.get('data') != null;
+  }.property('data'),
+
+  isOrange: function () {
+    return false;
+  }.property('data'),
+
+  isRed: function () {
+    return false;
+  }.property('data'),
+
+  timeConverter: function (timestamp) {
+    var origin = new Date(timestamp);
+    origin = origin.toString();
+    var result = [];
+    var start = origin.indexOf('GMT');
+    if (start == -1) { // ie
+      var arr = origin.split(" ");
+      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
+      var second = '';
+      for (var i = 4; i < arr.length; i++) {
+        second = second + " " + arr[i];
+      }
+      result.pushObject(second);
+    }
+    else { // other browsers
+      var end = origin.indexOf(" ", start);
+      result.pushObject(origin.slice(0, start-10));
+      result.pushObject(origin.slice(start-9));
+    }
+    return result;
+  },
+
+  /**
+   * All children should have such code
+   * <code>
+   * didInsertElement: function() {
+   *   this._super();
+   *   this.calc();
+   * }
+   * </code>
+   */
+  didInsertElement: function() {
+    this._super();
+    this.addObserver('model.' + this.get('modelField'), this, this.calc);
+  },
+
+  calc: function() {
+    this.set('data', this.calcData());
+    this.set('content', this.calcContent());
+  },
+
+  uptimeProcessing: function(uptime) {
+    var uptimeString = this.timeConverter(uptime);
+    var diff = (new Date()).getTime() - uptime;
+    if (diff < 0) {
+      diff = 0;
+    }
+    var formatted = date.timingFormat(diff); //17.67 days
+    var timeUnit = null;
+    switch (formatted.split(" ")[1]) {
+      case 'secs':
+        timeUnit = 's';
+        break;
+      case 'hours':
+        timeUnit = 'hr';
+        break;
+      case 'days':
+        timeUnit = 'd';
+        break;
+      case 'mins':
+        timeUnit = 'min';
+        break;
+      default:
+        timeUnit = formatted.split(" ")[1];
+    }
+    this.set('timeUnit', timeUnit);
+    this.set('hiddenInfo', []);
+    this.get('hiddenInfo').pushObject(formatted);
+    this.get('hiddenInfo').pushObject(uptimeString[0]);
+    this.get('hiddenInfo').pushObject(uptimeString[1]);
+    return formatted;
+  },
+
+  calcData: function () {
+    var field = this.get('modelField');
+    var uptime = this.get('model').get(field);
+    if (uptime && uptime > 0) {
+      var formatted = this.uptimeProcessing(uptime);
+      return parseFloat(formatted.split(" ")[0]);
+    }
+    this.set('hiddenInfo', [this.get('component'),'Not Running']);
+    return null;
+  },
+
+  calcContent: function () {
+    var data = this.get('data');
+    if (data) {
+      return data.toFixed(1) + ' '+ this.get('timeUnit');
+    }
+    else {
+      return Em.I18n.t('services.service.summary.notAvailable');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js b/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
index da96be0..e422abb 100644
--- a/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
+++ b/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
@@ -19,94 +19,21 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.YARNMemoryPieChartView = App.DashboardWidgetView.extend({
+App.YARNMemoryPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.YARNMemory'),
   id: '27',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
-  model_type: 'yarn',
-
-  hiddenInfo: function () {
-    var memUsed = this.get('model').get('yarnMemoryAllocated');
-    var memCommitted = this.get('model').get('yarnMemoryAvailable');
-    var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
-    var result = [];
-    result.pushObject(percent.toFixed(1) + '% used');
-    result.pushObject(numberUtils.bytesToSize(memUsed, 1, 'parseFloat', 1024 * 1024) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, 'parseFloat', 1024 * 1024));
-    return result;
-  }.property('model.yarnMemoryAllocated', 'model.yarnMemoryAvailable'),
-
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.yarnMemoryAvailable');
-    return total > 0 ;
-  }.property('model.yarnMemoryAvailable'),
-
-  content: App.ChartPieView.extend({
+  widgetHtmlId: 'widget-yarn-memory',
 
-    model: null,  //data bind here
-    id: 'widget-yarn-memory', // id in html
-    stroke: '#D6DDDF', //light grey
-    thresh1: null,
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var memUsed = this.get('model').get('yarnMemoryAllocated') * 1000000;
-      var memCommitted = this.get('model').get('yarnMemoryAvailable') * 1000000;
-      var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted).toFixed() : 0;
-      return [ percent, 100 - percent];
-    }.property('model.yarnMemoryAllocated', 'model.yarnMemoryAvailable'),
-
-    contentColor: function (){
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
+  model_type: 'yarn',
 
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
+  modelFieldUsed: 'yarnMemoryAllocated',
+  modelFieldMax: 'yarnMemoryAvailable',
 
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
 
-})
\ No newline at end of file
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widget_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widget_test.js b/ambari-web/test/views/main/dashboard/widget_test.js
new file mode 100644
index 0000000..f1e0436
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widget_test.js
@@ -0,0 +1,73 @@
+/**
+ * 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/main/dashboard/widget');
+
+describe('App.DashboardWidgetView', function() {
+  var dashboardWidgetView = App.DashboardWidgetView.create();
+
+  describe('#viewID', function() {
+    it('viewID is computed with id', function() {
+      dashboardWidgetView.set('id', 5);
+      expect(dashboardWidgetView.get('viewID')).to.equal('widget-5');
+    });
+  });
+
+  describe('#hoverContentTopClass', function() {
+    var tests = [
+      {
+        h: ['', ''],
+        e: 'simple-text-hidden-two-line',
+        m: '2 lines'
+      },
+      {
+        h: ['', '', ''],
+        e: 'simple-text-hidden-three-line',
+        m: '3 lines'
+      },
+      {
+        h: [''],
+        e: '',
+        m: '1 line'
+      },
+      {
+        h: [],
+        e: '',
+        m: '0 lines'
+      },
+      {
+        h: ['', '', '', '', ''],
+        e: 'simple-text-hidden-five-line',
+        m: '5 lines'
+      },
+      {
+        h: ['', '', '', ''],
+        e: 'simple-text-hidden-four-line',
+        m: '4 lines'
+      }
+    ];
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        dashboardWidgetView.set('hiddenInfo', test.h);
+        expect(dashboardWidgetView.get('hoverContentTopClass')).to.equal(test.e);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js b/ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js
new file mode 100644
index 0000000..e68a8e3
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js
@@ -0,0 +1,69 @@
+/**
+ * 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/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/datanode_live');
+
+describe('App.DataNodeUpView', function() {
+
+  var tests = [
+    {
+      data: 100,
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true
+      }
+    },
+    {
+      data: 0,
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false
+      }
+    },
+    {
+      data: 50,
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('', function() {
+      var dataNodeUpView = App.DataNodeUpView.create({model_type:null, data: test.data, content: test.data.toString()});
+      it('isRed', function() {
+        expect(dataNodeUpView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(dataNodeUpView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(dataNodeUpView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js b/ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js
new file mode 100644
index 0000000..932ff87
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js
@@ -0,0 +1,103 @@
+/**
+ * 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('messages');
+require('views/main/dashboard/widgets/hbase_average_load');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.HBaseAverageLoadView', function() {
+
+  var tests = [
+    {
+      model: {
+        averageLoad: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1'
+      }
+    },
+    {
+      model: {
+        averageLoad: 10
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '10'
+      }
+    },
+    {
+      model: {
+        averageLoad: 0
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '0'
+      }
+    },
+    {
+      model: {
+        averageLoad: null
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable')
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('averageLoad - ' + test.model.averageLoad, function() {
+      var hBaseAverageLoadView = App.HBaseAverageLoadView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(hBaseAverageLoadView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(hBaseAverageLoadView.get('data')).to.equal(test.model.averageLoad);
+      });
+      it('isRed', function() {
+        expect(hBaseAverageLoadView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(hBaseAverageLoadView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(hBaseAverageLoadView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(hBaseAverageLoadView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js b/ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js
new file mode 100644
index 0000000..1f365cf
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js
@@ -0,0 +1,95 @@
+/**
+ * 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('messages');
+require('views/main/dashboard/widgets/hbase_master_uptime');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.HBaseMasterUptimeView', function() {
+
+  var tests = [
+    {
+      model: Em.Object.create({
+        masterStartTime: ((new Date()).getTime() - 192.1*24*3600*1000)
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '192.1 d',
+        data: 192.1
+      }
+    },
+    {
+      model:  Em.Object.create({
+        masterStartTime: 0
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    },
+    {
+      model:  Em.Object.create({
+        masterStartTime: null
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    var hBaseMasterUptimeView = App.HBaseMasterUptimeView.create({model_type:null, model: test.model});
+    hBaseMasterUptimeView.calc();
+    describe('masterStartTime - ' + test.model.masterStartTime, function() {
+      it('content', function() {
+        expect(hBaseMasterUptimeView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(hBaseMasterUptimeView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(hBaseMasterUptimeView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(hBaseMasterUptimeView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(hBaseMasterUptimeView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(hBaseMasterUptimeView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js b/ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js
new file mode 100644
index 0000000..80a292b
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js
@@ -0,0 +1,102 @@
+/**
+ * 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/main/dashboard/widgets/hbase_regions_in_transition');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.HBaseRegionsInTransitionView', function() {
+
+  var tests = [
+    {
+      model: {
+        regionsInTransition: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1'
+      }
+    },
+    {
+      model: {
+        regionsInTransition: 10
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '10'
+      }
+    },
+    {
+      model: {
+        regionsInTransition: 0
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '0'
+      }
+    },
+    {
+      model: {
+        regionsInTransition: null
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: true,
+        content: 'null'
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('regionsInTransition - ' + test.model.regionsInTransition, function() {
+      var hBaseRegionsInTransitionView = App.HBaseRegionsInTransitionView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(hBaseRegionsInTransitionView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(hBaseRegionsInTransitionView.get('data')).to.equal(test.model.regionsInTransition);
+      });
+      it('isRed', function() {
+        expect(hBaseRegionsInTransitionView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(hBaseRegionsInTransitionView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(hBaseRegionsInTransitionView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(hBaseRegionsInTransitionView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js b/ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js
new file mode 100644
index 0000000..c1ef319
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js
@@ -0,0 +1,107 @@
+/**
+ * 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('messages');
+require('views/main/dashboard/widgets/jobtracker_rpc');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.JobTrackerRpcView', function() {
+
+  var tests = [
+    {
+      model: {
+        jobTrackerRpc: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1.00 ms',
+        data: '1.00'
+      }
+    },
+    {
+      model: {
+        jobTrackerRpc: 10
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '10.00 ms',
+        data: '10.00'
+      }
+    },
+    {
+      model: {
+        jobTrackerRpc: 0
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '0 ms',
+        data: 0
+      }
+    },
+    {
+      model: {
+        jobTrackerRpc: null
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('jobTrackerRpc - ' + test.model.jobTrackerRpc, function() {
+      var jobTrackerRpcView = App.JobTrackerRpcView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(jobTrackerRpcView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(jobTrackerRpcView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(jobTrackerRpcView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(jobTrackerRpcView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(jobTrackerRpcView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(jobTrackerRpcView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js b/ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js
new file mode 100644
index 0000000..820c653
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js
@@ -0,0 +1,95 @@
+/**
+ * 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('messages');
+require('views/main/dashboard/widgets/jobtracker_uptime');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.JobTrackerUptimeView', function() {
+
+  var tests = [
+    {
+      model: Em.Object.create({
+        jobTrackerStartTime: ((new Date()).getTime() - 192.1*24*3600*1000)
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '192.1 d',
+        data: 192.1
+      }
+    },
+    {
+      model: Em.Object.create({
+        jobTrackerStartTime: 0
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    },
+    {
+      model: Em.Object.create({
+        jobTrackerStartTime: null
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('jobTrackerStartTime - ' + test.model.jobTrackerStartTime, function() {
+      var jobTrackerUptimeView = App.JobTrackerUptimeView.create({model_type:null, model: test.model});
+      jobTrackerUptimeView.calc();
+      it('content', function() {
+        expect(jobTrackerUptimeView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(jobTrackerUptimeView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(jobTrackerUptimeView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(jobTrackerUptimeView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(jobTrackerUptimeView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(jobTrackerUptimeView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/links_widget_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/links_widget_test.js b/ambari-web/test/views/main/dashboard/widgets/links_widget_test.js
new file mode 100644
index 0000000..4796bde
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/links_widget_test.js
@@ -0,0 +1,45 @@
+/**
+ * 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('models/host_component');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/links_widget');
+
+describe('App.LinkDashboardWidgetView', function() {
+
+  var model = Em.Object.create({
+    field: Em.Object.create({
+      publicHostName: 'host1'
+    })
+  });
+  var linkDashboardWidgetView = App.LinkDashboardWidgetView.create({
+    model_type: null,
+    model: model,
+    port: 1234,
+    modelField: 'field'
+  });
+  linkDashboardWidgetView.calc();
+  describe('#webUrl', function() {
+    it('calc', function() {
+      expect(linkDashboardWidgetView.get('webUrl')).to.equal('http://host1:1234');
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js b/ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js
new file mode 100644
index 0000000..5852c2b
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js
@@ -0,0 +1,107 @@
+/**
+ * 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('utils/helper');
+require('views/common/chart/pie');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/pie_chart_widget');
+require('views/main/dashboard/widgets/namenode_cpu');
+
+describe('App.NameNodeCpuPieChartView', function() {
+
+  var model = Em.Object.create({
+    used: null,
+    max: null
+  });
+  var nameNodeCpuPieChartView = App.NameNodeCpuPieChartView.create({
+    model_type: null,
+    model: model,
+    modelFieldUsed: 'used',
+    modelFieldMax: 'max',
+    widgetHtmlId: 'fake'
+  });
+
+  nameNodeCpuPieChartView.calc();
+
+  describe('#calcIsPieExists', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          used: 1
+        }),
+        e: true,
+        m: 'Exists'
+      },
+      {
+        model: Em.Object.create({
+          used: 0
+        }),
+        e: true,
+        m: 'Exists'
+      },
+      {
+        model: Em.Object.create({}),
+        e: false,
+        m: 'Not exists'
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        nameNodeCpuPieChartView.set('model', test.model);
+        expect(nameNodeCpuPieChartView.calcIsPieExists()).to.equal(test.e);
+      });
+    });
+  });
+
+  describe('calcDataForPieChart', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          used: 0
+        }),
+        e: ['0.0', 100],
+        m: 'Nothing is used'
+      },
+      {
+        model: Em.Object.create({
+          used: 100
+        }),
+        e: ['100.0', 0],
+        m: 'All is used'
+      },
+      {
+        model: Em.Object.create({
+          used: 50
+        }),
+        e: ['50.0', 50],
+        m: 'Half is used'
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        nameNodeCpuPieChartView.set('model', test.model);
+        expect(nameNodeCpuPieChartView.calcDataForPieChart()).to.eql(test.e);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js b/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
new file mode 100644
index 0000000..14016e8
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
@@ -0,0 +1,107 @@
+/**
+ * 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('messages');
+require('views/main/dashboard/widgets/namenode_rpc');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.NameNodeRpcView', function() {
+
+  var tests = [
+    {
+      model: {
+        nameNodeRpc: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1.00 ms',
+        data: '1.00'
+      }
+    },
+    {
+      model: {
+        nameNodeRpc: 10
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '10.00 ms',
+        data: '10.00'
+      }
+    },
+    {
+      model: {
+        nameNodeRpc: 0
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '0 ms',
+        data: 0
+      }
+    },
+    {
+      model: {
+        nameNodeRpc: null
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('nameNodeRpc - ' + test.model.nameNodeRpc, function() {
+      var jobTrackerRpcView = App.NameNodeRpcView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(jobTrackerRpcView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(jobTrackerRpcView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(jobTrackerRpcView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(jobTrackerRpcView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(jobTrackerRpcView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(jobTrackerRpcView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js b/ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js
new file mode 100644
index 0000000..607ecfd
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js
@@ -0,0 +1,95 @@
+/**
+ * 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('messages');
+require('views/main/dashboard/widgets/namenode_uptime');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.NameNodeUptimeView', function() {
+
+  var tests = [
+    {
+      model: Em.Object.create({
+        nameNodeStartTime: ((new Date()).getTime() - 192.1*24*3600*1000)
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '192.1 d',
+        data: 192.1
+      }
+    },
+    {
+      model:  Em.Object.create({
+        nameNodeStartTime: 0
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    },
+    {
+      model:  Em.Object.create({
+        nameNodeStartTime: null
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    var nameNodeUptimeView = App.NameNodeUptimeView.create({model_type:null, model: test.model});
+    nameNodeUptimeView.calc();
+    describe('nameNodeStartTime - ' + test.model.nameNodeStartTime, function() {
+      it('content', function() {
+        expect(nameNodeUptimeView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(nameNodeUptimeView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(nameNodeUptimeView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(nameNodeUptimeView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(nameNodeUptimeView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(nameNodeUptimeView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js b/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js
new file mode 100644
index 0000000..ae15127
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js
@@ -0,0 +1,97 @@
+/**
+ * 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('messages');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/node_managers_live');
+
+describe('App.NodeManagersLiveView', function() {
+
+  var tests = [
+    {
+      model: {
+        nodeManagerNodes: [{}, {}, {}],
+        nodeManagerLiveNodes: [{}, {}]
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '2/3',
+        data: 67
+      }
+    },
+    {
+      model: {
+        nodeManagerNodes: [{},{}],
+        nodeManagerLiveNodes: [{},{}]
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '2/2',
+        data: 100
+      }
+    },
+    {
+      model: {
+        nodeManagerNodes: [{}, {}],
+        nodeManagerLiveNodes: []
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '0/2',
+        data: 0.00
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('nodeManagerNodes length - ' + test.model.nodeManagerNodes.length + ' | nodeManagerLiveNodes length' + test.model.nodeManagerLiveNodes.length, function() {
+      var nodeManagersLiveView = App.NodeManagersLiveView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(nodeManagersLiveView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(nodeManagersLiveView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(nodeManagersLiveView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(nodeManagersLiveView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(nodeManagersLiveView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(nodeManagersLiveView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});