You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2016/03/25 14:52:20 UTC

ambari git commit: AMBARI-15559 Cover services views with unit tests. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 93624db6d -> 2910f9f57


AMBARI-15559 Cover services views with unit tests. (atkach)


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

Branch: refs/heads/trunk
Commit: 2910f9f57e210985296819b71f67a1ea3c1f584f
Parents: 93624db
Author: Andrii Tkach <at...@hortonworks.com>
Authored: Thu Mar 24 17:14:58 2016 +0200
Committer: Andrii Tkach <at...@hortonworks.com>
Committed: Fri Mar 25 15:50:29 2016 +0200

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   5 +
 .../app/views/main/service/services/flume.js    |  33 +-
 .../app/views/main/service/services/hbase.js    |   4 +-
 .../app/views/main/service/services/hdfs.js     |  30 +-
 .../app/views/main/service/services/hive.js     |   4 +-
 .../app/views/main/service/services/yarn.js     |   4 +-
 .../views/main/service/services/zookeeper.js    |   8 +-
 .../views/main/service/services/flume_test.js   | 178 ++++++++++
 .../views/main/service/services/hbase_test.js   |  44 +++
 .../views/main/service/services/hdfs_test.js    | 342 ++++++++++++++++++-
 .../views/main/service/services/hive_test.js    |  44 +++
 .../main/service/services/mapreduce2_test.js    |  65 ++++
 .../views/main/service/services/oozie_test.js   |  55 +++
 .../views/main/service/services/storm_test.js   |  25 +-
 .../views/main/service/services/yarn_test.js    | 107 +++++-
 .../main/service/services/zookeeper_test.js     |  55 +++
 16 files changed, 958 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 6e33833..d114379 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -284,6 +284,11 @@ var files = [
   'test/views/main/service/services/ranger_test',
   'test/views/main/service/services/storm_test',
   'test/views/main/service/services/yarn_test',
+  'test/views/main/service/services/hive_test',
+  'test/views/main/service/services/oozie_test',
+  'test/views/main/service/services/mapreduce2_test',
+  'test/views/main/service/services/zookeeper_test',
+  'test/views/main/service/services/flume_test',
   'test/views/main/service/widgets/create/expression_view_test',
   'test/views/main/service/widgets/create/wizard_view_test',
   'test/views/main/service/widgets/create/step3_view_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/app/views/main/service/services/flume.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/services/flume.js b/ambari-web/app/views/main/service/services/flume.js
index 3f8c4cc..81379eb 100644
--- a/ambari-web/app/views/main/service/services/flume.js
+++ b/ambari-web/app/views/main/service/services/flume.js
@@ -29,14 +29,13 @@ App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServic
 
   selectedHost: null,
 
-  flumeAgentsCount:0,
+  flumeAgentsCount: 0,
 
   content: function () {
     var flumeAgents = this.get('service.agents'),
-    hostNames = flumeAgents.mapProperty('hostName').uniq(),
-    content = [],
-    self = this;
-    hostNames.forEach(function(hostName) {
+      content = [];
+
+    flumeAgents.mapProperty('hostName').uniq().forEach(function (hostName) {
       var agents = flumeAgents.filterProperty('hostName', hostName);
       content.push(
         Em.Object.create({
@@ -51,13 +50,11 @@ App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServic
     return content;
   }.property('App.FlumeAgent.@each.id', 'flumeAgentsCount'),
 
-
-
   summaryHeader: function () {
-    var agents = App.FlumeService.find().objectAt(0).get('agents'),
-    agentCount = agents.get('length'),
-    hostCount = this.get('service.flumeHandlersTotal');
-    return this.t("dashboard.services.flume.summary.title").format(hostCount, (hostCount > 1 ? "s" : ""), agentCount,  (agentCount > 1 ? "s" : ""));
+    var agentCount = App.FlumeService.find().objectAt(0).get('agents.length'),
+      hostCount = this.get('service.flumeHandlersTotal');
+    return this.t("dashboard.services.flume.summary.title")
+      .format(hostCount, (hostCount > 1 ? "s" : ""), agentCount, (agentCount > 1 ? "s" : ""));
   }.property('service.agents', 'service.hostComponents.length'),
 
   flumeHandlerComponent: Em.Object.create({
@@ -70,13 +67,13 @@ App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServic
 
     click: function (e) {
       var numberOfAgents = this.get('content.agents').length;
-      if($(e.target).attr('class') == "agent-host-name" || $(e.target).attr('class') == "agent-host-link"){
+      if ($(e.target).attr('class') == "agent-host-name" || $(e.target).attr('class') == "agent-host-link") {
         var currentTargetRow = $(e.currentTarget);
         if ($(e.target).attr('class') == "agent-host-link") {
           currentTargetRow = currentTargetRow.parent(".agent-host-name").parent();
         }
         currentTargetRow.parents("table:first").find('tr').removeClass('highlight');
-        currentTargetRow.addClass('highlight').nextAll("tr").slice(0,numberOfAgents - 1).addClass('highlight');
+        currentTargetRow.addClass('highlight').nextAll("tr").slice(0, numberOfAgents - 1).addClass('highlight');
         this.get('parentView').showAgentInfo(this.get('content'));
       }
     }
@@ -93,7 +90,7 @@ App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServic
   didInsertElement: function () {
     var self = this;
     this.filter();
-    this.$().on('click','.flume-agents-actions .dropdown-toggle', function (e){
+    this.$().on('click', '.flume-agents-actions .dropdown-toggle', function (e) {
       self.setDropdownPosition(this);
     });
   },
@@ -117,7 +114,7 @@ App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServic
    * Change classes for dropdown DOM elements after status change of selected agent
    */
   setActionsDropdownClasses: function () {
-    this.get('content').forEach(function(hosts){
+    this.get('content').forEach(function (hosts) {
       hosts.agents.forEach(function (agent) {
         agent.set('isStartAgentDisabled', agent.get('status') !== 'NOT_RUNNING');
         agent.set('isStopAgentDisabled', agent.get('status') !== 'RUNNING');
@@ -130,11 +127,11 @@ App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServic
   }.observes('service.agents.length'),
 
   /**
-   * Action handler from flume tepmlate.
+   * Action handler from flume template.
    * Highlight selected row and show metrics graphs of selected agent.
    *
    * @method showAgentInfo
-   * @param {object} agent
+   * @param {object} host
    */
   showAgentInfo: function (host) {
     this.set('selectedHost', host);
@@ -144,7 +141,7 @@ App.MainDashboardServiceFlumeView = App.TableView.extend(App.MainDashboardServic
    * Show Flume agent metric.
    *
    * @method setFlumeAgentMetric
-   * @param {object} agent - DS.model of agent
+   * @param {object} host
    */
   setAgentMetrics: function (host) {
     var mockMetricData = [

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/app/views/main/service/services/hbase.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/services/hbase.js b/ambari-web/app/views/main/service/services/hbase.js
index 24c6175..deb1a1c 100644
--- a/ambari-web/app/views/main/service/services/hbase.js
+++ b/ambari-web/app/views/main/service/services/hbase.js
@@ -70,8 +70,8 @@ App.MainDashboardServiceHbaseView = App.MainDashboardServiceView.extend({
   }.property('service.regionServersTotal', 'service.averageLoad'),
 
   hbaseMasterWebUrl: function () {
-    if (this.get('activeMaster.host') && this.get('activeMaster.host').get('publicHostName')) {
-      return "http://" + (App.singleNodeInstall ? App.singleNodeAlias : this.get('activeMaster.host').get('publicHostName')) + ":60010";
+    if (this.get('activeMaster.host.publicHostName')) {
+      return "http://" + (App.singleNodeInstall ? App.singleNodeAlias : this.get('activeMaster.host.publicHostName')) + ":60010";
     }
   }.property('activeMaster'),
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/app/views/main/service/services/hdfs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/services/hdfs.js b/ambari-web/app/views/main/service/services/hdfs.js
index dea9902..eb9b2a9 100644
--- a/ambari-web/app/views/main/service/services/hdfs.js
+++ b/ambari-web/app/views/main/service/services/hdfs.js
@@ -43,9 +43,8 @@ App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
       scheme: ['rgba(0,102,179,1)', 'rgba(0,102,179,0)']
     }),
     data: function () {
-      var total = this.get('service.capacityTotal') + 0;
-      var remaining = this.get('service.capacityRemaining') + 0;
-      var used = total - remaining;
+      var remaining = Number(this.get('service.capacityRemaining'));
+      var used = Number(this.get('service.capacityTotal')) - remaining;
       return [ used, remaining ];
     }.property('service.capacityUsed', 'service.capacityTotal')
   }),
@@ -107,7 +106,7 @@ App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
   blockErrorsMessage: Em.computed.i18nFormat('dashboard.services.hdfs.blockErrors', 'dfsCorruptBlocks', 'dfsMissingBlocks', 'dfsUnderReplicatedBlocks'),
 
   nodeUptime: function () {
-    var uptime = this.get('service').get('nameNodeStartTime');
+    var uptime = this.get('service.nameNodeStartTime');
     if (uptime && uptime > 0){
       var diff = App.dateTime() - uptime;
       if (diff < 0) {
@@ -120,7 +119,7 @@ App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
   }.property("service.nameNodeStartTime"),
 
   nodeWebUrl: function () {
-    return "http://" + (App.singleNodeInstall ? App.singleNodeAlias :  this.get('service').get('nameNode').get('publicHostName')) + ":50070";
+    return "http://" + (App.singleNodeInstall ? App.singleNodeAlias :  this.get('service.nameNode.publicHostName')) + ":50070";
   }.property('service.nameNode'),
 
   nodeHeap: App.MainDashboardServiceView.formattedHeap('dashboard.services.hdfs.nodes.heapUsed', 'service.jvmMemoryHeapUsed', 'service,jvmMemoryHeapMax'),
@@ -131,7 +130,7 @@ App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
     var total = this.get('service.capacityTotal');
     var remaining = this.get('service.capacityRemaining');
     var dfsUsed = this.get('service.capacityUsed');
-    return total !== null && remaining !== null && dfsUsed !== null ? total - remaining - dfsUsed : null;
+    return (Em.isNone(total) || Em.isNone(remaining) || Em.isNone(dfsUsed)) ? null : total - remaining - dfsUsed;
   }.property('service.capacityTotal', 'service.capacityRemaining', 'service.capacityUsed'),
 
   nonDfsUsedDisk: diskPart('dashboard.services.hdfs.capacityUsed', 'service.capacityTotal', 'nonDfsUsed'),
@@ -156,16 +155,23 @@ App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
   
   journalNodeComponent: Em.computed.alias('service.journalNodes.firstObject'),
 
+  /**
+   * @type {string}
+   */
   safeModeStatus: function () {
     var safeMode = this.get('service.safeModeStatus');
-    if (safeMode == null) {
+    if (Em.isNone(safeMode)) {
       return Em.I18n.t("services.service.summary.notAvailable");
-    } else if (safeMode.length == 0) {
+    } else if (safeMode.length === 0) {
       return Em.I18n.t("services.service.summary.safeModeStatus.notInSafeMode");
     } else {
       return Em.I18n.t("services.service.summary.safeModeStatus.inSafeMode");
     }
   }.property('service.safeModeStatus'),
+
+  /**
+   * @type {string}
+   */
   upgradeStatus: function () {
     var upgradeStatus = this.get('service.upgradeStatus');
     var healthStatus = this.get('service.healthStatus');
@@ -178,10 +184,12 @@ App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
       return Em.I18n.t("services.service.summary.notAvailable");
     }
   }.property('service.upgradeStatus', 'service.healthStatus'),
+
+  /**
+   * @type {boolean}
+   */
   isUpgradeStatusWarning: function () {
-    var upgradeStatus = this.get('service.upgradeStatus');
-    var healthStatus = this.get('service.healthStatus');
-    return upgradeStatus == 'false' && healthStatus == 'green';
+    return this.get('service.upgradeStatus') == 'false' && this.get('service.healthStatus') == 'green';
   }.property('service.upgradeStatus', 'service.healthStatus')
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/app/views/main/service/services/hive.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/services/hive.js b/ambari-web/app/views/main/service/services/hive.js
index 2b05e89..c1a0f58 100644
--- a/ambari-web/app/views/main/service/services/hive.js
+++ b/ambari-web/app/views/main/service/services/hive.js
@@ -22,8 +22,8 @@ App.MainDashboardServiceHiveView = App.MainDashboardServiceView.extend({
   templateName: require('templates/main/service/services/hive'),
   serviceName: 'hive',
 
-  titleMasters: function(){
+  titleMasters: function () {
     var masters = this.get('masters');
     return [masters.findProperty('componentName', 'HIVE_SERVER'), masters.findProperty('componentName', 'HIVE_METASTORE')];
-  }.property('service')
+  }.property('masters')
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/app/views/main/service/services/yarn.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/services/yarn.js b/ambari-web/app/views/main/service/services/yarn.js
index d4299ff..adb3eaf 100644
--- a/ambari-web/app/views/main/service/services/yarn.js
+++ b/ambari-web/app/views/main/service/services/yarn.js
@@ -36,8 +36,8 @@ App.MainDashboardServiceYARNView = App.MainDashboardServiceView.extend({
   hasManyYarnClients: Em.computed.gt('service.installedClients', 1),
 
   nodeUptime: function () {
-    var uptime = this.get('service').get('resourceManagerStartTime');
-    if (uptime && uptime > 0){
+    var uptime = this.get('service.resourceManagerStartTime');
+    if (uptime && uptime > 0) {
       var diff = App.dateTime() - uptime;
       if (diff < 0) {
         diff = 0;

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/app/views/main/service/services/zookeeper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/services/zookeeper.js b/ambari-web/app/views/main/service/services/zookeeper.js
index ee8259c..08f04e0 100644
--- a/ambari-web/app/views/main/service/services/zookeeper.js
+++ b/ambari-web/app/views/main/service/services/zookeeper.js
@@ -22,12 +22,12 @@ App.MainDashboardServiceZookeperView = App.MainDashboardServiceView.extend({
   templateName: require('templates/main/service/services/zookeeper'),
   serviceName: 'zookeeper',
 
-  titleInfo: function(){
+  titleInfo: function () {
     var components = this.get('service.hostComponents').filterProperty('componentName', 'ZOOKEEPER_SERVER');
     var running = 0;
-    components.forEach(function(item){
-      if(item.get('workStatus') === App.HostComponentStatus.started){
-        running += 1;
+    components.forEach(function (item) {
+      if (item.get('workStatus') === App.HostComponentStatus.started) {
+        running++;
       }
     });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/flume_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/flume_test.js b/ambari-web/test/views/main/service/services/flume_test.js
new file mode 100644
index 0000000..c6efdcb
--- /dev/null
+++ b/ambari-web/test/views/main/service/services/flume_test.js
@@ -0,0 +1,178 @@
+/**
+ * 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/service/services/flume');
+
+describe('App.MainDashboardServiceFlumeView', function () {
+  var view;
+
+  beforeEach(function() {
+    view = App.MainDashboardServiceFlumeView.create({
+      service: Em.Object.create({
+        agents: []
+      })
+    });
+  });
+
+  describe("#content", function() {
+
+    it("should return content", function() {
+      view.set('service.agents',  [
+        {hostName: 'host1'},
+        {hostName: 'host2'},
+        {hostName: 'host2'}
+      ]);
+      view.propertyDidChange('content');
+      expect(view.get('content').mapProperty('hostName')).to.be.eql(['host1', 'host2']);
+      expect(view.get('content').mapProperty('rowspan')).to.be.eql([1, 2]);
+      expect(view.get('content').mapProperty('firtstAgent')).to.be.eql([{hostName: 'host1'}, {hostName: 'host2'}]);
+    });
+  });
+
+  describe("#summaryHeader", function() {
+
+    beforeEach(function() {
+      this.mock = sinon.stub(App.FlumeService, 'find');
+    });
+    afterEach(function() {
+      this.mock.restore();
+    });
+
+    it("single host", function() {
+      this.mock.returns([
+        Em.Object.create({
+          agents: [{}]
+        })
+      ]);
+      view.set('service.flumeHandlersTotal', 1);
+      view.propertyDidChange('summaryHeader');
+      expect(view.get('summaryHeader')).to.be.equal(view.t("dashboard.services.flume.summary.title").format(1, "", 1, ""));
+    });
+
+    it("multiple hosts", function() {
+      this.mock.returns([
+        Em.Object.create({
+          agents: [{}, {}]
+        })
+      ]);
+      view.set('service.flumeHandlersTotal', 2);
+      view.propertyDidChange('summaryHeader');
+      expect(view.get('summaryHeader')).to.be.equal(view.t("dashboard.services.flume.summary.title").format(2, "s", 2, "s"));
+    });
+  });
+
+  describe("#didInsertElement()", function() {
+    var mock = {
+      on: function(a1, a2, callback) {
+        callback();
+      }
+    };
+
+    beforeEach(function() {
+      sinon.stub(view, 'filter');
+      sinon.stub(view, 'setDropdownPosition');
+      sinon.stub(mock, 'on');
+      sinon.stub(view, '$').returns(mock);
+      view.didInsertElement();
+    });
+    afterEach(function() {
+      view.filter.restore();
+      view.setDropdownPosition();
+      mock.on.restore();
+      view.$.restore();
+    });
+
+    it("filter should be called", function() {
+      expect(view.filter.calledOnce).to.be.true;
+    });
+
+    it("setDropdownPosition should be called", function() {
+      expect(view.filter.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#willDestroyElement()", function() {
+    var mock = {
+      off: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.stub(mock, 'off');
+      sinon.stub(view, '$').returns(mock);
+      view.willDestroyElement();
+    });
+    afterEach(function() {
+      mock.off.restore();
+      view.$.restore();
+    });
+
+    it("off should be called", function() {
+      expect(mock.off.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#setActionsDropdownClasses()", function() {
+
+    it("should disable dropdown", function() {
+      view.reopen({
+        content: [
+          Em.Object.create({
+            agents: [
+              Em.Object.create({status: 'RUNNING'}),
+              Em.Object.create({status: 'NOT_RUNNING'})
+            ]
+          })
+        ]
+      });
+      view.setActionsDropdownClasses();
+      expect(view.get('content')[0].get('agents')[0].get('isStartAgentDisabled')).to.be.true;
+      expect(view.get('content')[0].get('agents')[0].get('isStopAgentDisabled')).to.be.false;
+      expect(view.get('content')[0].get('agents')[1].get('isStartAgentDisabled')).to.be.false;
+      expect(view.get('content')[0].get('agents')[1].get('isStopAgentDisabled')).to.be.true;
+    });
+  });
+
+  describe("#updateFlumeAgentsCount()", function() {
+
+    it("should update flumeAgentsCount", function() {
+      view.set('service', Em.Object.create({
+        agents: [{}]
+      }));
+      view.updateFlumeAgentsCount();
+      expect(view.get('flumeAgentsCount')).to.be.equal(1);
+    });
+  });
+
+  describe("#showAgentInfo()", function() {
+
+    beforeEach(function() {
+      sinon.stub(view, 'setAgentMetrics');
+    });
+    afterEach(function() {
+      view.setAgentMetrics.restore();
+    });
+
+    it("setAgentMetrics should be called", function() {
+      var host = {hostName: 'host1'};
+      view.showAgentInfo(host);
+      expect(view.setAgentMetrics.calledWith(host)).to.be.true;
+      expect(view.get('selectedHost')).to.be.eql(host);
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/hbase_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/hbase_test.js b/ambari-web/test/views/main/service/services/hbase_test.js
index 2d4a4b9..6c0f1f1 100644
--- a/ambari-web/test/views/main/service/services/hbase_test.js
+++ b/ambari-web/test/views/main/service/services/hbase_test.js
@@ -65,4 +65,48 @@ describe('App.MainDashboardServiceHbaseView', function () {
 
   });
 
+  describe("#summaryHeader", function() {
+
+    it("averageLoad is NaN", function() {
+      view.set('service', Em.Object.create({
+        averageLoad: 'null',
+        regionServersTotal: 1
+      }));
+      view.propertyDidChange('summaryHeader');
+      expect(view.get('summaryHeader')).to.be.equal(view.t("dashboard.services.hbase.summary").format(1, view.t("services.service.summary.unknown")));
+    });
+
+    it("averageLoad is number", function() {
+      view.set('service', Em.Object.create({
+        averageLoad: 99,
+        regionServersTotal: 1
+      }));
+      view.propertyDidChange('summaryHeader');
+      expect(view.get('summaryHeader')).to.be.equal(view.t("dashboard.services.hbase.summary").format(1, 99));
+    });
+  });
+
+  describe("#hbaseMasterWebUrl", function() {
+
+    it("activeMaster is present", function() {
+      view.reopen({
+        activeMaster: Em.Object.create({
+          host: Em.Object.create({
+            publicHostName: 'host1'
+          })
+        })
+      });
+      App.set('singleNodeInstall', false);
+      view.propertyDidChange('hbaseMasterWebUrl');
+      expect(view.get('hbaseMasterWebUrl')).to.be.equal('http://host1:60010');
+    });
+    it("activeMaster is null", function() {
+      view.reopen({
+        activeMaster: null
+      });
+      view.propertyDidChange('hbaseMasterWebUrl');
+      expect(view.get('hbaseMasterWebUrl')).to.be.undefined;
+    });
+  });
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/hdfs_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/hdfs_test.js b/ambari-web/test/views/main/service/services/hdfs_test.js
index 9e6ebb8..688ce7c 100644
--- a/ambari-web/test/views/main/service/services/hdfs_test.js
+++ b/ambari-web/test/views/main/service/services/hdfs_test.js
@@ -17,13 +17,20 @@
  */
 
 var App = require('app');
+var date = require('utils/date/date');
+var numberUtils = require('utils/number_utils');
 require('/views/main/service/services/hdfs');
 
-function getView() {
-  return App.MainDashboardServiceHdfsView.create();
+function getView(options) {
+  return App.MainDashboardServiceHdfsView.create(options || {});
 }
 
 describe('App.MainDashboardServiceHdfsView', function () {
+  var view;
+
+  beforeEach(function() {
+    view = getView({service: Em.Object.create()});
+  });
 
   App.TestAliases.testAsComputedAlias(getView(), 'dataNodesDead', 'service.dataNodesInstalled', 'boolean');
 
@@ -31,4 +38,335 @@ describe('App.MainDashboardServiceHdfsView', function () {
 
   App.TestAliases.testAsComputedGt(getView(), 'showJournalNodes', 'service.journalNodes.length', 0);
 
+  describe("#Chart", function() {
+    var chartView;
+
+    beforeEach(function() {
+      chartView = view.get('Chart').create();
+    });
+
+    describe("#data", function () {
+
+      it("should return data", function () {
+        chartView.set('service', Em.Object.create({
+          capacityTotal: 100,
+          capacityRemaining: 1
+        }));
+        chartView.propertyDidChange('data');
+        expect(chartView.get('data')).to.be.eql([99, 1]);
+      });
+    });
+  });
+
+  describe("#dashboardMasterComponentView", function() {
+    var dashboardMasterComponentView;
+
+    beforeEach(function() {
+      dashboardMasterComponentView = view.get('dashboardMasterComponentView').create({
+        parentView: Em.Object.create()
+      });
+    });
+
+    describe("#mastersComp", function () {
+
+      it("should return master components", function () {
+        dashboardMasterComponentView.set('parentView.service', Em.Object.create({
+          hostComponents: [
+            Em.Object.create({
+              componentName: 'ZKFC'
+            }),
+            Em.Object.create({
+              componentName: 'JOURNALNODE'
+            }),
+            Em.Object.create({
+              componentName: 'NAMENODE',
+              isMaster: true
+            })
+          ]
+        }));
+        dashboardMasterComponentView.propertyDidChange('mastersComp');
+        expect(dashboardMasterComponentView.get('mastersComp').mapProperty('componentName')).to.be.eql(['NAMENODE', 'ZKFC']);
+        expect(dashboardMasterComponentView.get('mastersComp')[0].get('isMaster')).to.be.true;
+        expect(dashboardMasterComponentView.get('mastersComp')[1].get('isSubComponent')).to.be.true;
+      });
+    });
+
+    describe("#didInsertElement()", function() {
+
+      beforeEach(function() {
+        sinon.stub(App, 'tooltip');
+      });
+      afterEach(function() {
+        App.tooltip.restore();
+      });
+
+      it("App.tooltip should be called", function() {
+        dashboardMasterComponentView.didInsertElement();
+        expect(App.tooltip.calledOnce).to.be.true;
+      });
+    });
+
+    describe("#willDestroyElement()", function() {
+      var mock = {
+        tooltip: Em.K
+      };
+
+      beforeEach(function() {
+        sinon.stub(mock, 'tooltip');
+        sinon.stub(window, '$').returns(mock);
+      });
+      afterEach(function() {
+        mock.tooltip.restore();
+        window.$.restore();
+      });
+
+      it("tooltip destroy should be called", function() {
+        dashboardMasterComponentView.willDestroyElement();
+        expect(mock.tooltip.calledWith('destroy')).to.be.true;
+      });
+    });
+  });
+
+  describe("#didInsertElement()", function() {
+
+    beforeEach(function() {
+      sinon.stub(App, 'tooltip');
+    });
+    afterEach(function() {
+      App.tooltip.restore();
+    });
+
+    it("App.tooltip should be called", function() {
+      view.didInsertElement();
+      expect(App.tooltip.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#willDestroyElement()", function() {
+    var mock = {
+      tooltip: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.stub(mock, 'tooltip');
+      sinon.stub(window, '$').returns(mock);
+    });
+    afterEach(function() {
+      mock.tooltip.restore();
+      window.$.restore();
+    });
+
+    it("tooltip destroy should be called", function() {
+      view.willDestroyElement();
+      expect(mock.tooltip.calledWith('destroy')).to.be.true;
+    });
+  });
+
+  describe("#journalNodesLive", function() {
+
+    it("should return live journal nodes count", function() {
+      view.set('service', Em.Object.create({
+        journalNodes: [
+          Em.Object.create({workStatus: 'STARTED'}),
+          Em.Object.create()
+        ]
+      }));
+      view.propertyDidChange('journalNodesLive');
+      expect(view.get('journalNodesLive')).to.be.equal(1);
+    });
+  });
+
+  describe("#nodeUptime", function() {
+
+    beforeEach(function() {
+      sinon.stub(App, 'dateTime').returns(10);
+      sinon.stub(date, 'timingFormat').returns('11');
+    });
+    afterEach(function() {
+      App.dateTime.restore();
+      date.timingFormat.restore();
+    });
+
+    it("nameNodeStartTime is 0", function() {
+      view.set('service.nameNodeStartTime', 0);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('services.service.summary.notRunning'));
+    });
+
+    it("nameNodeStartTime is -1", function() {
+      view.set('service.nameNodeStartTime', -1);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('services.service.summary.notRunning'));
+    });
+
+    it("nameNodeStartTime is 1", function() {
+      view.set('service.nameNodeStartTime', 1);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('dashboard.services.uptime').format('11'));
+      expect(date.timingFormat.calledWith(9)).to.be.true;
+    });
+
+    it("nameNodeStartTime is 11", function() {
+      view.set('service.nameNodeStartTime', 11);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('dashboard.services.uptime').format('11'));
+      expect(date.timingFormat.calledWith(0)).to.be.true;
+    });
+  });
+
+  describe("#nodeWebUrl", function () {
+
+    it("singleNodeInstall is true", function () {
+      App.set('singleNodeInstall', true);
+      App.set('singleNodeAlias', 'host1');
+
+      view.propertyDidChange('nodeWebUrl');
+      expect(view.get('nodeWebUrl')).to.be.equal("http://host1:50070");
+    });
+
+    it("singleNodeInstall is false", function () {
+      App.set('singleNodeInstall', false);
+      view.set('service.nameNode', Em.Object.create({
+        publicHostName: 'host2'
+      }));
+      view.propertyDidChange('nodeWebUrl');
+      expect(view.get('nodeWebUrl')).to.be.equal("http://host2:50070");
+    });
+  });
+
+  describe("#nonDfsUsed", function() {
+    var testCases = [
+      {
+        input: {
+          capacityTotal: null,
+          capacityRemaining: 1,
+          capacityUsed: 90
+        },
+        expected: null
+      },
+      {
+        input: {
+          capacityTotal: 100,
+          capacityRemaining: null,
+          capacityUsed: 90
+        },
+        expected: null
+      },
+      {
+        input: {
+          capacityTotal: 100,
+          capacityRemaining: 1,
+          capacityUsed: null
+        },
+        expected: null
+      },
+      {
+        input: {
+          capacityTotal: 100,
+          capacityRemaining: 1,
+          capacityUsed: 90
+        },
+        expected: 9
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("total=" + test.input.capacityTotal + " remaining" + test.input.capacityRemaining + " used" + test.input.capacityUsed, function() {
+        view.get('service').setProperties(test.input);
+        view.propertyDidChange('nonDfsUsed');
+        expect(view.get('nonDfsUsed')).to.be.equal(test.expected);
+      });
+    });
+  });
+
+  describe("#isNfsInStack", function() {
+
+    beforeEach(function() {
+      this.mock = sinon.stub(App.StackServiceComponent, 'find');
+    });
+    afterEach(function() {
+      this.mock.restore();
+    });
+
+    it("no NFS_GATEWAY component", function() {
+      this.mock.returns([]);
+      view.propertyDidChange('isNfsInStack');
+      expect(view.get('isNfsInStack')).to.be.false;
+    });
+
+    it("NFS_GATEWAY component present", function() {
+      this.mock.returns([{componentName: 'NFS_GATEWAY'}]);
+      view.propertyDidChange('isNfsInStack');
+      expect(view.get('isNfsInStack')).to.be.true;
+    });
+  });
+
+  describe("#safeModeStatus", function() {
+
+    it("safeModeStatus is null", function() {
+      view.set('service.safeModeStatus', null);
+      view.propertyDidChange('safeModeStatus');
+      expect(view.get('safeModeStatus')).to.be.equal(Em.I18n.t("services.service.summary.notAvailable"));
+    });
+
+    it("safeModeStatus is empty", function() {
+      view.set('service.safeModeStatus', "");
+      view.propertyDidChange('safeModeStatus');
+      expect(view.get('safeModeStatus')).to.be.equal(Em.I18n.t("services.service.summary.safeModeStatus.notInSafeMode"));
+    });
+
+    it("safeModeStatus is on", function() {
+      view.set('service.safeModeStatus', 'on');
+      view.propertyDidChange('safeModeStatus');
+      expect(view.get('safeModeStatus')).to.be.equal(Em.I18n.t("services.service.summary.safeModeStatus.inSafeMode"));
+    });
+  });
+
+  describe("#upgradeStatus", function() {
+
+    it("upgradeStatus is 'true'", function() {
+      view.set('service.upgradeStatus', 'true');
+      view.propertyDidChange('upgradeStatus');
+      expect(view.get('upgradeStatus')).to.be.equal(Em.I18n.t('services.service.summary.pendingUpgradeStatus.notPending'));
+    });
+
+    it("upgradeStatus is 'false', healthStatus is 'green'", function() {
+      view.set('service.upgradeStatus', 'false');
+      view.set('service.healthStatus', 'green');
+      view.propertyDidChange('upgradeStatus');
+      expect(view.get('upgradeStatus')).to.be.equal(Em.I18n.t('services.service.summary.pendingUpgradeStatus.notFinalized'));
+    });
+
+    it("upgradeStatus is null", function() {
+      view.set('service.upgradeStatus', null);
+      view.propertyDidChange('upgradeStatus');
+      expect(view.get('upgradeStatus')).to.be.equal(Em.I18n.t('services.service.summary.notAvailable'));
+    });
+  });
+
+  describe("#isUpgradeStatusWarning", function() {
+
+    it("upgradeStatus is 'false', healthStatus is 'green'", function() {
+      view.set('service.upgradeStatus', 'false');
+      view.set('service.healthStatus', 'green');
+      view.propertyDidChange('isUpgradeStatusWarning');
+      expect(view.get('isUpgradeStatusWarning')).to.be.true;
+    });
+
+    it("upgradeStatus is 'true', healthStatus is 'green'", function() {
+      view.set('service.upgradeStatus', 'true');
+      view.set('service.healthStatus', 'green');
+      view.propertyDidChange('isUpgradeStatusWarning');
+      expect(view.get('isUpgradeStatusWarning')).to.be.false;
+    });
+
+    it("upgradeStatus is 'false', healthStatus is 'red'", function() {
+      view.set('service.upgradeStatus', 'false');
+      view.set('service.healthStatus', 'red');
+      view.propertyDidChange('isUpgradeStatusWarning');
+      expect(view.get('isUpgradeStatusWarning')).to.be.false;
+    });
+  });
+
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/hive_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/hive_test.js b/ambari-web/test/views/main/service/services/hive_test.js
new file mode 100644
index 0000000..a31ba62
--- /dev/null
+++ b/ambari-web/test/views/main/service/services/hive_test.js
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('/views/main/service/services/hive');
+
+describe('App.MainDashboardServiceHiveView', function () {
+  var view;
+
+  beforeEach(function() {
+    view = App.MainDashboardServiceHiveView.create();
+  });
+
+  describe("#titleMasters", function () {
+
+    it("should pass components", function () {
+      view.set('masters', [
+        Em.Object.create({
+          componentName: 'HIVE_SERVER'
+        }),
+        Em.Object.create({
+          componentName: 'HIVE_METASTORE'
+        })
+      ]);
+      view.propertyDidChange('titleMasters');
+      expect(view.get('titleMasters')).to.be.eql([view.get('masters')[0], view.get('masters')[1]]);
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/mapreduce2_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/mapreduce2_test.js b/ambari-web/test/views/main/service/services/mapreduce2_test.js
new file mode 100644
index 0000000..c4d3fcf
--- /dev/null
+++ b/ambari-web/test/views/main/service/services/mapreduce2_test.js
@@ -0,0 +1,65 @@
+/**
+ * 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/service/services/mapreduce2');
+
+describe('App.MainDashboardServiceMapreduce2View', function () {
+  var view;
+
+  beforeEach(function() {
+    view = App.MainDashboardServiceMapreduce2View.create();
+  });
+
+ describe("#titleInfo", function() {
+
+   it("HISTORYSERVER stopped", function() {
+     view.set('service', Em.Object.create({
+       hostComponents: [
+         Em.Object.create({
+           componentName: 'HISTORYSERVER',
+           workStatus: App.HostComponentStatus.stopped
+         })
+       ]
+     }));
+     view.propertyDidChange('titleInfo');
+     expect(view.get('titleInfo')).to.be.equal(view.t('services.mapreduce2.history.stopped'));
+   });
+
+   it("HISTORYSERVER absent", function() {
+     view.set('service', Em.Object.create({
+       hostComponents: []
+     }));
+     view.propertyDidChange('titleInfo');
+     expect(view.get('titleInfo')).to.be.equal(view.t('services.mapreduce2.history.unknown'));
+   });
+
+   it("HISTORYSERVER started", function() {
+     view.set('service', Em.Object.create({
+       hostComponents: [
+         Em.Object.create({
+           componentName: 'HISTORYSERVER',
+           workStatus: App.HostComponentStatus.started
+         })
+       ]
+     }));
+     view.propertyDidChange('titleInfo');
+     expect(view.get('titleInfo')).to.be.equal(view.t('services.mapreduce2.history.running'));
+   });
+ });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/oozie_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/oozie_test.js b/ambari-web/test/views/main/service/services/oozie_test.js
new file mode 100644
index 0000000..c95e65f
--- /dev/null
+++ b/ambari-web/test/views/main/service/services/oozie_test.js
@@ -0,0 +1,55 @@
+/**
+ * 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/service/services/oozie');
+
+describe('App.MainDashboardServiceOozieView', function () {
+  var view;
+
+  beforeEach(function() {
+    view = App.MainDashboardServiceOozieView.create();
+  });
+
+  describe("#webUi", function () {
+
+    it("singleNodeInstall is true", function () {
+      App.set('singleNodeInstall', true);
+      App.set('singleNodeAlias', 'host1');
+
+      view.propertyDidChange('webUi');
+      expect(view.get('webUi')).to.be.equal("http://host1:11000/oozie");
+    });
+
+    it("singleNodeInstall is false", function () {
+      App.set('singleNodeInstall', false);
+      view.set('service', Em.Object.create({
+        hostComponents: [
+          Em.Object.create({
+            componentName: 'OOZIE_SERVER',
+            host: Em.Object.create({
+              publicHostName: 'host2'
+            })
+          })
+        ]
+      }));
+      view.propertyDidChange('webUi');
+      expect(view.get('webUi')).to.be.equal("http://host2:11000/oozie");
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/storm_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/storm_test.js b/ambari-web/test/views/main/service/services/storm_test.js
index e659e78..d839d8b 100644
--- a/ambari-web/test/views/main/service/services/storm_test.js
+++ b/ambari-web/test/views/main/service/services/storm_test.js
@@ -19,11 +19,16 @@
 var App = require('app');
 require('/views/main/service/services/storm');
 
-function getView() {
-  return App.MainDashboardServiceStormView.create();
+function getView(options) {
+  return App.MainDashboardServiceStormView.create(options || {});
 }
 
 describe('App.MainDashboardServiceStormView', function () {
+  var view;
+
+  beforeEach(function() {
+    view = getView();
+  });
 
   App.TestAliases.testAsComputedPercents(getView(), 'freeSlotsPercentage', 'service.freeSlots', 'service.totalSlots');
 
@@ -31,4 +36,20 @@ describe('App.MainDashboardServiceStormView', function () {
 
   App.TestAliases.testAsComputedAlias(getView(), 'superVisorsTotal', 'service.superVisorsTotal');
 
+  describe("#nimbusUptimeFormatted", function () {
+
+    it("nimbusUptime is set", function () {
+      view.set('service', Em.Object.create({
+        nimbusUptime: '1'
+      }));
+      view.propertyDidChange('nimbusUptimeFormatted');
+      expect(view.get('nimbusUptimeFormatted')).to.be.equal('1');
+    });
+
+    it("nimbusUptime is not set", function () {
+      view.set('service', null);
+      view.propertyDidChange('nimbusUptimeFormatted');
+      expect(view.get('nimbusUptimeFormatted')).to.be.equal(Em.I18n.t('services.service.summary.notRunning'));
+    });
+  });
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/yarn_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/yarn_test.js b/ambari-web/test/views/main/service/services/yarn_test.js
index b9eee96..9c1cf7a 100644
--- a/ambari-web/test/views/main/service/services/yarn_test.js
+++ b/ambari-web/test/views/main/service/services/yarn_test.js
@@ -17,13 +17,21 @@
  */
 
 var App = require('app');
+var date = require('utils/date/date');
+var numberUtils = require('utils/number_utils');
+
 require('/views/main/service/services/yarn');
 
-function getView() {
-  return App.MainDashboardServiceYARNView.create();
+function getView(options) {
+  return App.MainDashboardServiceYARNView.create(options || {});
 }
 
 describe('App.MainDashboardServiceYARNView', function () {
+  var view;
+
+  beforeEach(function() {
+    view = getView({service: Em.Object.create()});
+  });
 
   App.TestAliases.testAsComputedCountBasedMessage(getView(), 'nodeManagerText', 'service.nodeManagersTotal', '', Em.I18n.t('services.service.summary.viewHost'), Em.I18n.t('services.service.summary.viewHosts'));
 
@@ -48,4 +56,99 @@ describe('App.MainDashboardServiceYARNView', function () {
 
   App.TestAliases.testAsComputedFormatNa(getView(), '_queuesCountFormatted', 'service.queuesCount');
 
+  describe("#nodeUptime", function() {
+
+    beforeEach(function() {
+      sinon.stub(App, 'dateTime').returns(10);
+      sinon.stub(date, 'timingFormat').returns('11');
+    });
+    afterEach(function() {
+      App.dateTime.restore();
+      date.timingFormat.restore();
+    });
+
+    it("resourceManagerStartTime is 0", function() {
+      view.set('service.resourceManagerStartTime', 0);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('services.service.summary.notRunning'));
+    });
+
+    it("resourceManagerStartTime is -1", function() {
+      view.set('service.resourceManagerStartTime', -1);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('services.service.summary.notRunning'));
+    });
+
+    it("resourceManagerStartTime is 1", function() {
+      view.set('service.resourceManagerStartTime', 1);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('dashboard.services.uptime').format('11'));
+      expect(date.timingFormat.calledWith(9)).to.be.true;
+    });
+
+    it("resourceManagerStartTime is 11", function() {
+      view.set('service.resourceManagerStartTime', 11);
+      view.propertyDidChange('nodeUptime');
+      expect(view.get('nodeUptime')).to.be.equal(view.t('dashboard.services.uptime').format('11'));
+      expect(date.timingFormat.calledWith(0)).to.be.true;
+    });
+  });
+
+  describe("#memory", function() {
+
+    beforeEach(function() {
+      sinon.stub(numberUtils, 'bytesToSize', function(arg1) {
+        return arg1;
+      })
+    });
+    afterEach(function() {
+      numberUtils.bytesToSize.restore();
+    });
+
+    it("should return formatted memory", function() {
+      view.set('service', Em.Object.create({
+        allocatedMemory: 1,
+        reservedMemory: 2,
+        availableMemory: 3
+      }));
+      view.propertyDidChange('memory');
+      expect(view.get('memory')).to.be.equal(Em.I18n.t('dashboard.services.yarn.memory.msg').format(1,2,3));
+    });
+  });
+
+  describe("#didInsertElement()", function() {
+
+    beforeEach(function() {
+      sinon.stub(App, 'tooltip');
+    });
+    afterEach(function() {
+      App.tooltip.restore();
+    });
+
+    it("App.tooltip should be called", function() {
+      view.didInsertElement();
+      expect(App.tooltip.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#willDestroyElement()", function() {
+    var mock = {
+      tooltip: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.stub(window, '$').returns(mock);
+      sinon.stub(mock, 'tooltip');
+    });
+    afterEach(function() {
+      window.$.restore();
+      mock.tooltip.restore();
+    });
+
+    it("tooltip destroy should be called", function() {
+      view.willDestroyElement();
+      expect(mock.tooltip.calledWith('destroy')).to.be.true;
+    });
+  });
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2910f9f5/ambari-web/test/views/main/service/services/zookeeper_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/services/zookeeper_test.js b/ambari-web/test/views/main/service/services/zookeeper_test.js
new file mode 100644
index 0000000..0c3f344
--- /dev/null
+++ b/ambari-web/test/views/main/service/services/zookeeper_test.js
@@ -0,0 +1,55 @@
+/**
+ * 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/service/services/zookeeper');
+
+describe('App.MainDashboardServiceZookeperView', function () {
+  var view;
+
+  beforeEach(function () {
+    view = App.MainDashboardServiceZookeperView.create();
+  });
+
+  describe("#titleInfo", function () {
+
+    it("should return ZOOKEEPER info", function () {
+      view.set('service', Em.Object.create({
+        hostComponents: [
+          Em.Object.create({
+            componentName: 'ZOOKEEPER_SERVER',
+            workStatus: App.HostComponentStatus.stopped
+          }),
+          Em.Object.create({
+            componentName: 'ZOOKEEPER_SERVER',
+            workStatus: App.HostComponentStatus.started
+          })
+        ]
+      }));
+      view.propertyDidChange('titleInfo');
+      expect(view.get('titleInfo')).to.be.eql({
+        pre: view.t('services.zookeeper.prefix').format(1),
+        title: view.t('services.zookeeper.title').format(2),
+        component: Em.Object.create({
+          componentName: 'ZOOKEEPER_SERVER',
+          workStatus: App.HostComponentStatus.stopped
+        })
+      });
+    });
+  });
+});
\ No newline at end of file