Browse Source

AMBARI-5442. Tests coverage for helper.js and component.js. (Buzhor Denys via alexantonenko)

Alex Antonenko 11 years ago
parent
commit
0661a4b398

+ 4 - 4
ambari-web/app/app.js

@@ -89,14 +89,12 @@ module.exports = Em.Application.create({
   /**
   /**
    * List of disabled components for the current stack with related info.
    * List of disabled components for the current stack with related info.
    * Each element has followed structure:
    * Each element has followed structure:
-   * @type {Em.Object}
+   * @type {Em.Enumerable.<Em.Object>}
    *   @property componentName {String} - name of the component
    *   @property componentName {String} - name of the component
    *   @property properties {Object} - mapped properties by site files,
    *   @property properties {Object} - mapped properties by site files,
    *    for example:
    *    for example:
    *      properties: { global_properties: [], site_properties: [], etc. }
    *      properties: { global_properties: [], site_properties: [], etc. }
    *   @property reviewConfigs {Ember.Object} - reference review_configs.js
    *   @property reviewConfigs {Ember.Object} - reference review_configs.js
-   *
-   * @type {Array}
    */
    */
   stackDependedComponents: [],
   stackDependedComponents: [],
 
 
@@ -191,6 +189,8 @@ module.exports = Em.Application.create({
    * otherwise enable component and remove it from stackDependedComponents
    * otherwise enable component and remove it from stackDependedComponents
    * Check forbidden/allowed components and
    * Check forbidden/allowed components and
    * remove/restore related data.
    * remove/restore related data.
+   *
+   * @method handleStackDependedComponents
    */
    */
   handleStackDependedComponents: function () {
   handleStackDependedComponents: function () {
     // need for unit testing and test mode
     // need for unit testing and test mode
@@ -256,4 +256,4 @@ module.exports = Em.Application.create({
       clients: self.StackServiceComponent.find().filterProperty('isClient',true).mapProperty('componentName')
       clients: self.StackServiceComponent.find().filterProperty('isClient',true).mapProperty('componentName')
     })
     })
   }.property()
   }.property()
-});
+});

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

@@ -86,6 +86,8 @@ require('test/utils/validator_test');
 require('test/utils/config_test');
 require('test/utils/config_test');
 require('test/utils/string_utils_test');
 require('test/utils/string_utils_test');
 require('test/utils/lazy_loading_test');
 require('test/utils/lazy_loading_test');
+require('test/utils/helper_test');
+require('test/utils/component_test');
 require('test/views/common/chart/linear_time_test');
 require('test/views/common/chart/linear_time_test');
 require('test/views/common/filter_view_test');
 require('test/views/common/filter_view_test');
 require('test/views/common/table_view_test');
 require('test/views/common/table_view_test');

+ 6 - 1
ambari-web/app/utils/component.js

@@ -35,6 +35,8 @@ module.exports = {
    *    isClient : false
    *    isClient : false
    * }]
    * }]
    *
    *
+   *  @method getInstalledComponents
+   *  @return {object[]}
    */
    */
   getInstalledComponents : function(){
   getInstalledComponents : function(){
     var components = App.HostComponent.find();
     var components = App.HostComponent.find();
@@ -57,8 +59,11 @@ module.exports = {
   },
   },
 
 
   /**
   /**
+   * Format and load info about components to StackServiceComponent model.
    *
    *
-   * @param data
+   * @method loadStackServiceComponentModel
+   * @param data {object} response from server
+   * @return {object} formatted info about components
    */
    */
   loadStackServiceComponentModel: function(data) {
   loadStackServiceComponentModel: function(data) {
     var serviceComponents = {items: []};
     var serviceComponents = {items: []};

+ 135 - 38
ambari-web/app/utils/helper.js

@@ -16,22 +16,52 @@
  * limitations under the License.
  * limitations under the License.
  */
  */
 
 
+/**
+ * Remove spaces at beginning and ending of line.
+ * @example
+ *  var str = "  I'm a string  "
+ *  str.trim() // return "I'm a string"
+ * @method trim
+ * @return {string}
+ */
 String.prototype.trim = function () {
 String.prototype.trim = function () {
   return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
   return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
 };
 };
-
+/**
+ * Determines whether string end within another string.
+ *
+ * @method endsWith
+ * @param suffix {string}  substring for search
+ * @return {boolean}
+ */
 String.prototype.endsWith = function(suffix) {
 String.prototype.endsWith = function(suffix) {
   return this.indexOf(suffix, this.length - suffix.length) !== -1;
   return this.indexOf(suffix, this.length - suffix.length) !== -1;
 };
 };
-
+/**
+ * Determines whether string start within another string.
+ *
+ * @method startsWith
+ * @param prefix {string} substring for search
+ * @return {boolean}
+ */
 String.prototype.startsWith = function (prefix){
 String.prototype.startsWith = function (prefix){
   return this.indexOf(prefix) == 0;
   return this.indexOf(prefix) == 0;
 };
 };
-
+/**
+ * Determines whether string founded within another string.
+ *
+ * @method contains
+ * @param substring {string} substring for search
+ * @return {boolean}
+ */
 String.prototype.contains = function(substring) {
 String.prototype.contains = function(substring) {
   return this.indexOf(substring) != -1;
   return this.indexOf(substring) != -1;
 };
 };
-
+/**
+ * Capitalize the first letter of string.
+ * @method capitalize
+ * @return {string}
+ */
 String.prototype.capitalize = function () {
 String.prototype.capitalize = function () {
   return this.charAt(0).toUpperCase() + this.slice(1);
   return this.charAt(0).toUpperCase() + this.slice(1);
 };
 };
@@ -49,9 +79,10 @@ String.prototype.capitalize = function () {
  *  tofind.findIn(person, 1); // 'yes'
  *  tofind.findIn(person, 1); // 'yes'
  *  tofind.findIn(person, 2); // null
  *  tofind.findIn(person, 2); // null
  *
  *
- *  @param multi  Object
- *  @param index  Occurrence count of this key
- *  @return Value of key at given index
+ *  @method findIn
+ *  @param multi {object}
+ *  @param index {number} Occurrence count of this key
+ *  @return {*} Value of key at given index
  */
  */
 String.prototype.findIn = function(multi, index, _foundValues) {
 String.prototype.findIn = function(multi, index, _foundValues) {
   if (!index) {
   if (!index) {
@@ -82,10 +113,14 @@ String.prototype.findIn = function(multi, index, _foundValues) {
   }
   }
   return value;
   return value;
 };
 };
-
 /**
 /**
- * Replace {i} with argument. where i is number of argument to replace with
- * @return {String}
+ * Replace {i} with argument. where i is number of argument to replace with.
+ * @example
+ *  var str = "{0} world{1}";
+ *  str.format("Hello", "!") // return "Hello world!"
+ *
+ * @method format
+ * @return {string}
  */
  */
 String.prototype.format = function () {
 String.prototype.format = function () {
   var args = arguments;
   var args = arguments;
@@ -93,7 +128,14 @@ String.prototype.format = function () {
     return typeof args[number] != 'undefined' ? args[number] : match;
     return typeof args[number] != 'undefined' ? args[number] : match;
   });
   });
 };
 };
-
+/**
+ * Wrap words in string within template.
+ *
+ * @method highlight
+ * @param {string[]} words - words to wrap
+ * @param {string} [highlightTemplate="<b>{0}</b>"] - template for wrapping
+ * @return {string}
+ */
 String.prototype.highlight = function (words, highlightTemplate) {
 String.prototype.highlight = function (words, highlightTemplate) {
   var self = this;
   var self = this;
   highlightTemplate = highlightTemplate ? highlightTemplate : "<b>{0}</b>";
   highlightTemplate = highlightTemplate ? highlightTemplate : "<b>{0}</b>";
@@ -107,7 +149,20 @@ String.prototype.highlight = function (words, highlightTemplate) {
 
 
   return self;
   return self;
 };
 };
-
+/**
+ * Convert time in milliseconds to object contained days, hours and minutes.
+ * @typedef ConvertedTime
+ *  @type {Object}
+ *  @property {number} d - days
+ *  @property {number} h - hours
+ *  @property {string} m - minutes
+ * @example
+ *  var time = 1000000000;
+ *  time.toDaysHoursMinutes() // {d: 11, h: 13, m: "46.67"}
+ *
+ * @method toDaysHoursMinutes
+ * @return {object}
+ */
 Number.prototype.toDaysHoursMinutes = function () {
 Number.prototype.toDaysHoursMinutes = function () {
   var formatted = {},
   var formatted = {},
     dateDiff = this,
     dateDiff = this,
@@ -125,7 +180,6 @@ Number.prototype.toDaysHoursMinutes = function () {
 
 
   return formatted;
   return formatted;
 };
 };
-
 /**
 /**
  Sort an array by the key specified in the argument.
  Sort an array by the key specified in the argument.
  Handle only native js objects as element of array, not the Ember's object.
  Handle only native js objects as element of array, not the Ember's object.
@@ -152,13 +206,14 @@ Array.prototype.sortPropertyLight = function (path) {
   });
   });
   return this;
   return this;
 };
 };
-
+/** @namespace Em **/
 Em.CoreObject.reopen({
 Em.CoreObject.reopen({
   t:function (key, attrs) {
   t:function (key, attrs) {
     return Em.I18n.t(key, attrs)
     return Em.I18n.t(key, attrs)
   }
   }
 });
 });
 
 
+/** @namespace Em.Handlebars **/
 Em.Handlebars.registerHelper('log', function (variable) {
 Em.Handlebars.registerHelper('log', function (variable) {
   console.log(variable);
   console.log(variable);
 });
 });
@@ -186,12 +241,19 @@ Em.Handlebars.registerHelper('highlight', function (property, words, fn) {
 
 
   return new Em.Handlebars.SafeString(property);
   return new Em.Handlebars.SafeString(property);
 });
 });
-
+/**
+ * @namespace App
+ */
 App = require('app');
 App = require('app');
 
 
 /**
 /**
  * Certain variables can have JSON in string
  * Certain variables can have JSON in string
  * format, or in JSON format itself.
  * format, or in JSON format itself.
+ *
+ * @memberof App
+ * @function parseJson
+ * @param {string|object}
+ * @return {object}
  */
  */
 App.parseJSON = function (value) {
 App.parseJSON = function (value) {
   if (typeof value == "string") {
   if (typeof value == "string") {
@@ -203,8 +265,9 @@ App.parseJSON = function (value) {
  * Check for empty <code>Object</code>, built in Em.isEmpty()
  * Check for empty <code>Object</code>, built in Em.isEmpty()
  * doesn't support <code>Object</code> type
  * doesn't support <code>Object</code> type
  *
  *
- * @params obj {Object}
- *
+ * @memberof App
+ * @method isEmptyObject
+ * @param obj {Object}
  * @return {Boolean}
  * @return {Boolean}
  */
  */
 App.isEmptyObject = function(obj) {
 App.isEmptyObject = function(obj) {
@@ -212,11 +275,16 @@ App.isEmptyObject = function(obj) {
   for (var prop in obj) { if (obj.hasOwnProperty(prop)) {empty = false; break;} }
   for (var prop in obj) { if (obj.hasOwnProperty(prop)) {empty = false; break;} }
   return empty;
   return empty;
 }
 }
-
+/**
+ *
+ * @namespace App
+ * @namespace App.format
+ */
 App.format = {
 App.format = {
-
   /**
   /**
-   * @type Object
+   * @memberof App.format
+   * @type {object}
+   * @property components
    */
    */
   components: {
   components: {
     'APP_TIMELINE_SERVER': 'App Timeline Server',
     'APP_TIMELINE_SERVER': 'App Timeline Server',
@@ -294,7 +362,9 @@ App.format = {
   },
   },
 
 
   /**
   /**
-   * @type Object
+   * @memberof App.format
+   * @property command
+   * @type {object}
    */
    */
   command: {
   command: {
     'INSTALL': 'Install',
     'INSTALL': 'Install',
@@ -312,7 +382,11 @@ App.format = {
 
 
   /**
   /**
    * convert role to readable string
    * convert role to readable string
-   * @param role
+   *
+   * @memberof App.format
+   * @method role
+   * @param {string} role
+   * return {string}
    */
    */
   role:function (role) {
   role:function (role) {
     return this.components[role] ? this.components[role] : '';
     return this.components[role] ? this.components[role] : '';
@@ -320,7 +394,11 @@ App.format = {
 
 
   /**
   /**
    * convert command_detail to readable string, show the string for all tasks name
    * convert command_detail to readable string, show the string for all tasks name
-   * @param command_detail
+   *
+   * @memberof App.format
+   * @method commandDetail
+   * @param {string} command_detail
+   * @return {string}
    */
    */
   commandDetail: function (command_detail) {
   commandDetail: function (command_detail) {
     var detailArr = command_detail.split(' ');
     var detailArr = command_detail.split(' ');
@@ -350,13 +428,21 @@ App.format = {
   },
   },
 
 
   /**
   /**
-   * PENDING - Not queued yet for a host
-   * QUEUED - Queued for a host
-   * IN_PROGRESS - Host reported it is working
-   * COMPLETED - Host reported success
-   * FAILED - Failed
-   * TIMEDOUT - Host did not respond in time
-   * ABORTED - Operation was abandoned
+   * Convert uppercase status name to downcase.
+   * <br>
+   * <br>PENDING - Not queued yet for a host
+   * <br>QUEUED - Queued for a host
+   * <br>IN_PROGRESS - Host reported it is working
+   * <br>COMPLETED - Host reported success
+   * <br>FAILED - Failed
+   * <br>TIMEDOUT - Host did not respond in time
+   * <br>ABORTED - Operation was abandoned
+   *
+   * @memberof App.format
+   * @method taskStatus
+   * @param {string} _taskStatus
+   * @return {string}
+   *
    */
    */
   taskStatus:function (_taskStatus) {
   taskStatus:function (_taskStatus) {
     return _taskStatus.toLowerCase();
     return _taskStatus.toLowerCase();
@@ -366,8 +452,11 @@ App.format = {
 /**
 /**
  * wrapper to bootstrap popover
  * wrapper to bootstrap popover
  * fix issue when popover stuck on view routing
  * fix issue when popover stuck on view routing
- * @param self
- * @param options
+ *
+ * @memberof App
+ * @method popover
+ * @param {DOMElement} self
+ * @param {object} options
  */
  */
 App.popover = function(self, options) {
 App.popover = function(self, options) {
   self.popover(options);
   self.popover(options);
@@ -379,11 +468,14 @@ App.popover = function(self, options) {
 /**
 /**
  * wrapper to bootstrap tooltip
  * wrapper to bootstrap tooltip
  * fix issue when tooltip stuck on view routing
  * fix issue when tooltip stuck on view routing
- * @param self - DOM element
- * @param options
+ * @memberof App
+ * @method tooltip
+ * @param {DOMElement} self
+ * @param {object} options
  */
  */
 App.tooltip = function(self, options) {
 App.tooltip = function(self, options) {
   self.tooltip(options);
   self.tooltip(options);
+  /* istanbul ignore next */
   self.on("remove", function () {
   self.on("remove", function () {
     $(this).trigger('mouseleave');
     $(this).trigger('mouseleave');
   });
   });
@@ -392,16 +484,21 @@ App.tooltip = function(self, options) {
 /**
 /**
  * wrapper to Date().getTime()
  * wrapper to Date().getTime()
  * fix issue when client clock and server clock not sync
  * fix issue when client clock and server clock not sync
- * @return timeStamp of current server clock
+ *
+ * @memberof App
+ * @method dateTime
+ * @return {Number} timeStamp of current server clock
  */
  */
 App.dateTime = function() {
 App.dateTime = function() {
   return new Date().getTime() + App.clockDistance;
   return new Date().getTime() + App.clockDistance;
 };
 };
 
 
-/*
+/**
  * Helper function for bound property helper registration
  * Helper function for bound property helper registration
- * @params name {String} - name of helper
- * @params view {Em.View} - view
+ * @memberof App
+ * @method registerBoundHelper
+ * @param name {String} name of helper
+ * @param view {Em.View} view
  */
  */
 App.registerBoundHelper = function(name, view) {
 App.registerBoundHelper = function(name, view) {
   Em.Handlebars.registerHelper(name, function(property, options) {
   Em.Handlebars.registerHelper(name, function(property, options) {

+ 1 - 0
ambari-web/karma.conf.js

@@ -150,6 +150,7 @@ module.exports = function(config) {
     // If browser does not capture in given timeout [ms], kill it
     // If browser does not capture in given timeout [ms], kill it
     captureTimeout: 60000,
     captureTimeout: 60000,
 
 
+    browserNoActivityTimeout: 30000,
 
 
     // Continuous Integration mode
     // Continuous Integration mode
     // if true, it capture browsers, run tests and exit
     // if true, it capture browsers, run tests and exit

+ 9 - 0
ambari-web/test/app_test.js

@@ -20,6 +20,7 @@ var App = require('app');
 require('views/common/quick_view_link_view');
 require('views/common/quick_view_link_view');
 require('models/host_component');
 require('models/host_component');
 require('models/stack_service_component');
 require('models/stack_service_component');
+var modelSetup = require('test/init_model_test');
 
 
 describe('#App', function() {
 describe('#App', function() {
 
 
@@ -363,6 +364,14 @@ describe('#App', function() {
 
 
   describe('#handleStackDependedComponents()', function () {
   describe('#handleStackDependedComponents()', function () {
 
 
+    beforeEach(function(){
+      modelSetup.setupStackServiceComponent();
+    });
+
+    afterEach(function(){
+      modelSetup.cleanStackServiceComponent();
+    });
+
     it('if handleStackDependencyTest is true then stackDependedComponents should be unmodified', function () {
     it('if handleStackDependencyTest is true then stackDependedComponents should be unmodified', function () {
       App.set('testMode', false);
       App.set('testMode', false);
       App.set('handleStackDependencyTest', true);
       App.set('handleStackDependencyTest', true);

+ 2 - 2
ambari-web/test/controllers/global/cluster_controller_test.js

@@ -232,7 +232,7 @@ describe('App.clusterController', function () {
     it('singleNodeInstall = true', function () {
     it('singleNodeInstall = true', function () {
       App.set('singleNodeInstall', true);
       App.set('singleNodeInstall', true);
       controller.set('gangliaWebProtocol', '');
       controller.set('gangliaWebProtocol', '');
-      expect(controller.get('gangliaUrl')).to.equal("http://:42080/ganglia");
+      expect(controller.get('gangliaUrl')).to.equal("http://" + location.hostname + ":42080/ganglia");
     });
     });
     it('singleNodeAlias is "alias"', function () {
     it('singleNodeAlias is "alias"', function () {
       App.set('singleNodeAlias', 'alias');
       App.set('singleNodeAlias', 'alias');
@@ -449,4 +449,4 @@ describe('App.clusterController', function () {
     });
     });
   });
   });
 
 
-});
+});

+ 18 - 11
ambari-web/test/init_model_test.js

@@ -21,14 +21,21 @@ require('models/stack_service_component');
 require('mappers/server_data_mapper');
 require('mappers/server_data_mapper');
 require('mappers/stack_service_component_mapper');
 require('mappers/stack_service_component_mapper');
 
 
-/**
- * initialization of App.StackServiceComponent model
- * @type {*}
- */
-var data = {items: Em.A([])};
-require('test/service_components').items.forEach(function(i) {
-  i.serviceComponents.forEach(function(sc) {
-    data.items.pushObject(sc.StackServiceComponents);
-  });
-});
-App.stackServiceComponentMapper.map(data);
+module.exports = {
+  setupStackServiceComponent: function() {
+    /**
+     * initialization of App.StackServiceComponent model
+     * @type {*}
+     */
+    var data = {items: Em.A([])};
+    require('test/service_components').items.forEach(function(i) {
+      i.serviceComponents.forEach(function(sc) {
+        data.items.pushObject(sc.StackServiceComponents);
+      });
+    });
+    App.stackServiceComponentMapper.map(data);
+  },
+  cleanStackServiceComponent: function(){
+    App.StackServiceComponent.find().set('content',[]);
+  }
+};

+ 7 - 1
ambari-web/test/installer/step9_test.js

@@ -23,9 +23,15 @@ require('models/stack_service_component');
 require('models/hosts');
 require('models/hosts');
 require('controllers/wizard/step9_controller');
 require('controllers/wizard/step9_controller');
 require('utils/helper');
 require('utils/helper');
+var modelSetup = require('test/init_model_test');
 
 
 describe('App.InstallerStep9Controller', function () {
 describe('App.InstallerStep9Controller', function () {
-
+  beforeEach(function(){
+    modelSetup.setupStackServiceComponent()
+  });
+  afterEach(function(){
+    modelSetup.cleanStackServiceComponent();
+  })
   describe('#isSubmitDisabled', function () {
   describe('#isSubmitDisabled', function () {
     var tests = Em.A([
     var tests = Em.A([
       {controllerName: 'addHostController', state: 'STARTED', e: false},
       {controllerName: 'addHostController', state: 'STARTED', e: false},

+ 121 - 0
ambari-web/test/utils/component_test.js

@@ -0,0 +1,121 @@
+/**
+ * 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 component = require('utils/component');
+require('models/host_component');
+require('models/stack_service_component');
+
+describe('utils/component', function(){
+  describe('#getInstalledComponents()', function(){
+    beforeEach(function(){
+      App.HostComponent.find().set('content',[]);
+      App.store.loadMany(App.HostComponent, [
+        {
+          "component_name" : "HISTORYSERVER",
+          "is_client" : false,
+          "is_master" : true,
+          "is_slave" : false
+        },
+        {
+          "component_name" : "TASKTRACKER",
+          "is_client" : false,
+          "is_master" : false,
+          "is_slave" : true
+        }
+      ]);
+    });
+    afterEach(function(){
+      App.HostComponent.find().set('content',[]);
+    });
+    it('names of components should be equal for input and output arrays', function(){
+      expect(component.getInstalledComponents().mapProperty('id')).to.have.members(App.HostComponent.find().mapProperty('componentName'));
+    });
+  });
+  describe('#loadStackServiceComponentModel()', function(){
+    var data = {
+      "items": [
+        {
+          "serviceComponents": [
+            {
+              "StackServiceComponents": {
+                "component_category": "CLIENT",
+                "component_name": "FALCON_CLIENT",
+                "is_client": true,
+                "is_master": false,
+                "service_name": "FALCON",
+                "stack_name": "HDP",
+                "stack_version": "2.1"
+              }
+            },
+            {
+              "StackServiceComponents": {
+                "component_category": "MASTER",
+                "component_name": "FALCON_SERVER",
+                "is_client": false,
+                "is_master": true,
+                "service_name": "FALCON",
+                "stack_name": "HDP",
+                "stack_version": "2.1"
+              }
+            }
+          ]
+        },
+        {
+          "serviceComponents": [
+            {
+              "StackServiceComponents": {
+                "component_category": "SLAVE",
+                "component_name": "GANGLIA_MONITOR",
+                "is_client": false,
+                "is_master": false,
+                "service_name": "GANGLIA",
+                "stack_name": "HDP",
+                "stack_version": "2.1"
+              }
+            },
+            {
+              "StackServiceComponents": {
+                "component_category": "MASTER",
+                "component_name": "GANGLIA_SERVER",
+                "is_client": false,
+                "is_master": true,
+                "service_name": "GANGLIA",
+                "stack_name": "HDP",
+                "stack_version": "2.1"
+              }
+            }
+          ]
+        }
+      ]
+    };
+
+    afterEach(function(){
+      App.StackServiceComponent.find().set('content', []);
+    });
+
+    it('should return 4 components', function(){
+      expect(component.loadStackServiceComponentModel(data).items.length).to.eql(4);
+    });
+
+    it('should load data to StackServiceComponent model', function(){
+      component.loadStackServiceComponentModel(data);
+      expect(App.StackServiceComponent.find().get('content')).have.length(4);
+    });
+  });
+});

+ 209 - 0
ambari-web/test/utils/helper_test.js

@@ -0,0 +1,209 @@
+/**
+ * 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');
+
+describe('utils/helper', function() {
+  describe('String helpers', function() {
+    describe('#trim()', function(){
+      it('should replace first space', function() {
+        expect(' as d f'.trim()).to.eql('as d f');
+      });
+    });
+    describe('#endsWith()', function() {
+      it('`abcd` ends with `d`', function(){
+        expect('abcd'.endsWith('d')).to.eql(true);
+      });
+      it('`abcd` doesn\'t end with `f`', function(){
+        expect('abcd'.endsWith('f')).to.eql(false);
+      });
+    });
+    describe('#contains()', function() {
+      it('`abc` contains b', function(){
+        expect('abc'.contains('b')).to.eql(true);
+      });
+      it('`abc` doesn\'t contain d', function() {
+        expect('abc'.contains('d')).to.eql(false);
+      });
+    });
+    describe('#capitalize()',function() {
+      it('`abc d` should start with `A`', function() {
+        expect('abc d'.capitalize()).to.eql('Abc d');
+      });
+    });
+    describe('#findIn()', function(){
+      var obj = {
+        a: {
+          a1: 'AVal1'
+        },
+        b: 'BVal',
+        c: {
+          c1: {
+            c2: 'Cval2'
+          },
+          b: 'BVal'
+        }
+      };
+      var testValue = function(key, value) {
+        it('key `' + key + '` should have `' + JSON.stringify(value) + '` value', function() {
+          expect(key.findIn(obj)).to.eql(value);
+        });
+      };
+      it('expect return `null` on non-object input', function(){
+        expect('a'.findIn('b')).to.null;
+      });
+      testValue('a', obj.a);
+      testValue('c2', obj.c.c1.c2);
+      testValue('b', obj.b);
+      testValue('d', null);
+    });
+    describe('#format()', function(){
+      it('should replace string correctly', function(){
+        expect("{0} world{1}".format("Hello","!")).to.eql("Hello world!");
+      });
+    });
+    describe('#highlight()', function() {
+      var str = "Hello world! I want to highlight this word!";
+      it('should highlight `word` with default template', function() {
+        var result = str.highlight(['word']);
+        expect(result).to.eql("Hello world! I want to highlight this <b>word</b>!");
+      });
+      it('should highlight `world` and `word` with template `<span class="yellow">{0}</span>`', function() {
+        var result = str.highlight(["world", "word"], '<span class="yellow">{0}</span>');
+        expect(result).to.eql('Hello <span class="yellow">world</span>! I want to highlight this <span class="yellow">word</span>!')
+      });
+      var str2 = "First word, second word";
+      it('should highlight `word` multiply times with default template', function() {
+        var result = str2.highlight(["word"]);
+        expect(result).to.eql("First <b>word</b>, second <b>word</b>");
+      });
+    });
+  });
+  describe('Number helpers', function(){
+    describe('#toDaysHoursMinutes()', function(){
+      var time = 1000000000;
+      var minute = 1000*60;
+      var hour = 60*minute;
+      var day = 24*hour;
+      var result = time.toDaysHoursMinutes();
+      var testDays = Math.floor(time/day);
+      it('should correct convert days', function(){
+        expect(testDays).to.eql(result.d);
+      });
+      it('should correct convert hours', function(){
+        expect(Math.floor((time - testDays * day)/hour)).to.eql(result.h);
+      });
+      it('should correct convert minutes', function(){
+        expect(((time - Math.floor((time - testDays*day)/hour)*hour - testDays*day)/minute).toFixed(2)).to.eql(result.m);
+      });
+    });
+  });
+  describe('Array helpers', function(){
+    describe('#sortPropertyLight()', function(){
+      var testable = [
+        { a: 2 },
+        { a: 1 },
+        { a: 6},
+        { a: 64},
+        { a: 3},
+        { a: 3}
+      ];
+      var result = testable.sortPropertyLight('a');
+      it('should return array with same length', function(){
+        expect(testable.length).to.eql(result.length);
+      });
+      it('should sort array', function() {
+        result.forEach(function(resultObj, index, resultArr) {
+          if (index > resultArr.length - 1)
+            expect(resultObj.a < resultArr[index + 1].a).to.eql(false);
+        });
+      });
+      it('should try to sort without throwing exception', function(){
+        expect(testable.sortPropertyLight(['a'])).to.ok;
+      });
+    });
+  });
+  describe('App helpers', function(){
+    var appendDiv = function() {
+      $('body').append('<div id="tooltip-test"></div>');
+    };
+    var removeDiv = function() {
+      $('body').remove('#tooltip-test');
+    };
+    describe('#isEmptyObject', function(){
+      it('should return true on empty object', function() {
+        expect(App.isEmptyObject({})).to.eql(true);
+      });
+      it('should return false on non-empty object', function() {
+        expect(App.isEmptyObject({ a: 1 })).to.eql(false);
+      });
+    });
+    describe('#parseJSON()', function(){
+      var testable = '{"hello": "world"}';
+      expect(App.parseJSON(testable).hello).to.eql('world');
+    });
+    describe('#tooltip()', function() {
+      beforeEach(appendDiv);
+      afterEach(removeDiv);
+      it('should add tooltip', function() {
+        var tooltip = App.tooltip($('#tooltip-test'));
+        expect($('#tooltip-test').data('tooltip').enabled).to.eql(true);
+      });
+    });
+    describe('#popover()', function() {
+      beforeEach(appendDiv);
+      afterEach(removeDiv);
+      it('should add popover', function() {
+        var tooltip = App.popover($('#tooltip-test'));
+        expect($('#tooltip-test').data('popover').enabled).to.eql(true);
+      });
+    });
+    describe('#App.format', function(){
+      describe('#commandDetail()', function() {
+        var command = "GANGLIA_MONITOR STOP";
+        var ignored = "DECOMMISSION, NAMENODE";
+        var removeString = "SERVICE/HDFS STOP";
+        var nagiosState = "nagios_update_ignore ACTIONEXECUTE";
+        it('should convert command to readable info', function() {
+          expect(App.format.commandDetail(command)).to.eql(' Ganglia Monitor Stop');
+        });
+        it('should ignore decommission command', function(){
+          expect(App.format.commandDetail(ignored)).to.eql('  NameNode');
+        });
+        it('should remove SERVICE string from command', function(){
+          expect(App.format.commandDetail(removeString)).to.eql(' HDFS Stop');
+        });
+        it('should return maintenance message', function() {
+          expect(App.format.commandDetail(nagiosState)).to.eql(' Toggle Maintenance Mode');
+        });
+      });
+      describe('#taskStatus()', function(){
+        var testable = [
+          { status: 'PENDING', expectable: 'pending'},
+          { status: 'QUEUED', expectable: 'queued'},
+          { status: 'COMPLETED', expectable: 'completed'}
+        ];
+        testable.forEach(function(testObj){
+          it('should convert `' + testObj.status + '` to `' + testObj.expectable + '`', function(){
+            expect(App.format.taskStatus(testObj.status)).to.eql(testObj.expectable);
+          });
+        });
+      });
+    });
+  });
+});

+ 14 - 1
ambari-web/test/views/main/host/summary_test.js

@@ -25,6 +25,7 @@ require('views/main/host/summary');
 
 
 var mainHostSummaryView;
 var mainHostSummaryView;
 var extendedMainHostSummaryView = App.MainHostSummaryView.extend({content: {}, addToolTip: function(){}, installedServices: []});
 var extendedMainHostSummaryView = App.MainHostSummaryView.extend({content: {}, addToolTip: function(){}, installedServices: []});
+var modelSetup = require('test/init_model_test');
 
 
 describe('App.MainHostSummaryView', function() {
 describe('App.MainHostSummaryView', function() {
 
 
@@ -268,6 +269,13 @@ describe('App.MainHostSummaryView', function() {
 
 
   describe('#installableClientComponents', function() {
   describe('#installableClientComponents', function() {
 
 
+    beforeEach(function(){
+      modelSetup.setupStackServiceComponent();
+    });
+    afterEach(function(){
+      modelSetup.cleanStackServiceComponent();
+    });
+
     it('delete host not supported', function() {
     it('delete host not supported', function() {
       App.set('supports.deleteHost', false);
       App.set('supports.deleteHost', false);
       expect(mainHostSummaryView.get('installableClientComponents')).to.eql([]);
       expect(mainHostSummaryView.get('installableClientComponents')).to.eql([]);
@@ -327,7 +335,12 @@ describe('App.MainHostSummaryView', function() {
   });
   });
 
 
   describe('#addableComponents', function() {
   describe('#addableComponents', function() {
-
+    beforeEach(function(){
+      modelSetup.setupStackServiceComponent();
+    });
+    afterEach(function(){
+      modelSetup.cleanStackServiceComponent();
+    });
     var tests = Em.A([
     var tests = Em.A([
       {
       {
         content: Em.Object.create({
         content: Em.Object.create({