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

ambari git commit: AMBARI-15284. Make bound helpers testable (onechiporenko)

Repository: ambari
Updated Branches:
  refs/heads/trunk 93a2fe4f9 -> 1158f9342


AMBARI-15284. Make bound helpers testable (onechiporenko)


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

Branch: refs/heads/trunk
Commit: 1158f9342921d07d09316205678352addbf9e7b3
Parents: 93a2fe4
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Thu Mar 3 15:29:08 2016 +0200
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Thu Mar 3 15:49:26 2016 +0200

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   2 +
 ambari-web/app/initialize.js                    |   1 +
 ambari-web/app/utils/handlebars_helpers.js      |  97 ++++++++
 ambari-web/app/utils/helper.js                  | 220 -------------------
 ambari-web/app/views.js                         |   4 +
 .../views/common/helpers/format_null_view.js    |  30 +++
 .../common/helpers/format_word_break_view.js    |  52 +++++
 .../app/views/common/helpers/pluralize_view.js  | 107 +++++++++
 .../views/common/helpers/status_icon_view.js    |  62 ++++++
 .../helpers/format_word_break_view_test.js      |  54 +++++
 10 files changed, 409 insertions(+), 220 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/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 a875656..54013f8 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -26,6 +26,7 @@ require('mixins');
 require('models');
 require('controllers');
 require('views');
+require('utils/handlebars_helpers');
 require('router');
 require('mappers');
 
@@ -197,6 +198,7 @@ var files = [
   'test/views/common/configs/widgets/list_config_widget_view_test',
   'test/views/common/configs/widgets/slider_config_widget_view_test',
   'test/views/common/configs/widgets/toggle_config_widget_view_test',
+  'test/views/common/helpers/format_word_break_view_test',
   'test/views/common/ajax_default_error_popup_body_test',
   'test/views/common/filter_combo_cleanable_test',
   'test/views/common/filter_view_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/initialize.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/initialize.js b/ambari-web/app/initialize.js
index a749adf..3d05066 100644
--- a/ambari-web/app/initialize.js
+++ b/ambari-web/app/initialize.js
@@ -36,6 +36,7 @@ require('models');
 require('controllers');
 require('templates');
 require('views');
+require('utils/handlebars_helpers');
 require('router');
 
 require('utils/ajax/ajax');

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/utils/handlebars_helpers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/handlebars_helpers.js b/ambari-web/app/utils/handlebars_helpers.js
new file mode 100644
index 0000000..fe48b1e
--- /dev/null
+++ b/ambari-web/app/utils/handlebars_helpers.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');
+
+/**
+ * Helper function for bound property helper registration
+ * @memberof App
+ * @method registerBoundHelper
+ * @param name {String} name of helper
+ * @param view {Em.View} view
+ */
+App.registerBoundHelper = function(name, view) {
+  Em.Handlebars.registerHelper(name, function(property, options) {
+    options.hash.contentBinding = property;
+    return Em.Handlebars.helpers.view.call(this, view, options);
+  });
+};
+
+/*
+ * Return singular or plural word based on Em.I18n, view|controller context property key.
+ *
+ *  Example: {{pluralize hostsCount singular="t:host" plural="t:hosts"}}
+ *           {{pluralize hostsCount singular="@view.hostName"}}
+ */
+App.registerBoundHelper('pluralize', App.PluralizeView);
+/**
+ * Return defined string instead of empty if value is null/undefined
+ * by default is `n/a`.
+ *
+ * @param empty {String} - value instead of empty string (not required)
+ *  can be used with Em.I18n pass value started with't:'
+ *
+ * Examples:
+ *
+ * default value will be returned
+ * {{formatNull service.someValue}}
+ *
+ * <code>empty<code> will be returned
+ * {{formatNull service.someValue empty="I'm empty"}}
+ *
+ * Em.I18n translation will be returned
+ * {{formatNull service.someValue empty="t:my.key.to.translate"
+ */
+App.registerBoundHelper('formatNull', App.FormatNullView);
+
+/**
+ * Return formatted string with inserted <code>wbr</code>-tag after each dot or each '_'
+ *
+ * @param {String} content
+ *
+ * Examples:
+ *
+ * returns 'apple'
+ * {{formatWordBreak 'apple'}}
+ *
+ * returns 'apple.<wbr />banana'
+ * {{formatWordBreak 'apple.banana'}}
+ *
+ * returns 'apple.<wbr />banana.<wbr />uranium'
+ * {{formatWordBreak 'apple.banana.uranium'}}
+ *
+ * returns 'apple_<wbr />banana_<wbr />uranium'
+ * {{formatWordBreak 'apple_banana_uranium'}}
+ *
+ * returns 'Very<wbr />Long<wbr />String<wbr />With<wbr />Uppercase'
+ * {{formatWordBreak 'VeryLongStringWithUppercase'}}
+ */
+App.registerBoundHelper('formatWordBreak', App.FormatWordBreakView);
+
+/**
+ * Return <i></i> with class that correspond to status
+ *
+ * @param {string} content - status
+ *
+ * Examples:
+ *
+ * {{statusIcon view.status}}
+ * returns 'icon-cog'
+ *
+ */
+App.registerBoundHelper('statusIcon', App.StatusIconView);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/utils/helper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/helper.js b/ambari-web/app/utils/helper.js
index ff5fe11..eb73667 100644
--- a/ambari-web/app/utils/helper.js
+++ b/ambari-web/app/utils/helper.js
@@ -814,226 +814,6 @@ App.getTimeStampFromLocalTime = function (time) {
 };
 
 /**
- * Helper function for bound property helper registration
- * @memberof App
- * @method registerBoundHelper
- * @param name {String} name of helper
- * @param view {Em.View} view
- */
-App.registerBoundHelper = function(name, view) {
-  Em.Handlebars.registerHelper(name, function(property, options) {
-    options.hash.contentBinding = property;
-    return Em.Handlebars.helpers.view.call(this, view, options);
-  });
-};
-
-/*
- * Return singular or plural word based on Em.I18n, view|controller context property key.
- *
- *  Example: {{pluralize hostsCount singular="t:host" plural="t:hosts"}}
- *           {{pluralize hostsCount singular="@view.hostName"}}
- */
-App.registerBoundHelper('pluralize', Em.View.extend({
-  tagName: 'span',
-  template: Em.Handlebars.compile('{{view.wordOut}}'),
-
-  wordOut: function() {
-    var count, singular, plural;
-    count = this.get('content');
-    singular = this.get('singular');
-    plural = this.get('plural');
-    return this.getWord(count, singular, plural);
-  }.property('content'),
-  /**
-   * Get computed word.
-   *
-   * @param {Number} count
-   * @param {String} singular
-   * @param {String} [plural]
-   * @return {String}
-   * @method getWord
-   */
-  getWord: function(count, singular, plural) {
-    singular = this.parseValue(singular);
-    // if plural not passed
-    if (!plural) plural = singular + 's';
-    else plural = this.parseValue(plural);
-    if (singular && plural) {
-      if (count == 1) {
-        return singular;
-      } else {
-        return plural;
-      }
-    }
-    return '';
-  },
-  /**
-   * Detect and return value from its instance.
-   *
-   * @param {String} value
-   * @return {*}
-   * @method parseValue
-   **/
-  parseValue: function(value) {
-    switch (value[0]) {
-      case '@':
-        value = this.getViewPropertyValue(value);
-        break;
-      case 't':
-        value = this.tDetect(value);
-        break;
-      default:
-        break;
-    }
-    return value;
-  },
-  /*
-   * Detect for Em.I18n.t reference call
-   * @params word {String}
-   * return {String}
-  */
-  tDetect: function(word) {
-    var splitted = word.split(':');
-    if (splitted.length > 1 && splitted[0] == 't') {
-      return Em.I18n.t(splitted[1]);
-    } else {
-      return splitted[0];
-    }
-  },
-  /**
-   * Get property value from view|controller by its key path.
-   *
-   * @param {String} value - key path
-   * @return {*}
-   * @method getViewPropertyValue
-   **/
-  getViewPropertyValue: function(value) {
-    value = value.substr(1);
-    var keyword = value.split('.')[0]; // return 'controller' or 'view'
-    switch (keyword) {
-      case 'controller':
-        return Em.get(this, value);
-      case 'view':
-        return Em.get(this, value.replace(/^view/, 'parentView'));
-      default:
-        break;
-    }
-  }
-  })
-);
-/**
- * Return defined string instead of empty if value is null/undefined
- * by default is `n/a`.
- *
- * @param empty {String} - value instead of empty string (not required)
- *  can be used with Em.I18n pass value started with't:'
- *
- * Examples:
- *
- * default value will be returned
- * {{formatNull service.someValue}}
- *
- * <code>empty<code> will be returned
- * {{formatNull service.someValue empty="I'm empty"}}
- *
- * Em.I18n translation will be returned
- * {{formatNull service.someValue empty="t:my.key.to.translate"
- */
-App.registerBoundHelper('formatNull', Em.View.extend({
-  tagName: 'span',
-  template: Em.Handlebars.compile('{{view.result}}'),
-
-  result: function() {
-    var emptyValue = this.get('empty') ? this.get('empty') : Em.I18n.t('services.service.summary.notAvailable');
-    emptyValue = emptyValue.startsWith('t:') ? Em.I18n.t(emptyValue.substr(2, emptyValue.length)) : emptyValue;
-    return (this.get('content') || this.get('content') == 0) ? this.get('content') : emptyValue;
-  }.property('content')
-}));
-
-/**
- * Return formatted string with inserted <code>wbr</code>-tag after each dot
- *
- * @param {String} content
- *
- * Examples:
- *
- * returns 'apple'
- * {{formatWordBreak 'apple'}}
- *
- * returns 'apple.<wbr />banana'
- * {{formatWordBreak 'apple.banana'}}
- *
- * returns 'apple.<wbr />banana.<wbr />uranium'
- * {{formatWordBreak 'apple.banana.uranium'}}
- */
-App.registerBoundHelper('formatWordBreak', Em.View.extend({
-  attributeBindings: ["data-original-title"],
-  tagName: 'span',
-  template: Em.Handlebars.compile('{{{view.result}}}'),
-
-  /**
-   * @type {string}
-   */
-  result: function() {
-    return this.get('content') && this.get('content').replace(/\./g, '.<wbr />');
-  }.property('content')
-}));
-
-/**
- * Return <i></i> with class that correspond to status
- *
- * @param {string} content - status
- *
- * Examples:
- *
- * {{statusIcon view.status}}
- * returns 'icon-cog'
- *
- */
-App.registerBoundHelper('statusIcon', Em.View.extend({
-  tagName: 'i',
-
-  /**
-   * relation map between status and icon class
-   * @type {object}
-   */
-  statusIconMap: {
-    'COMPLETED': 'icon-ok completed',
-    'WARNING': 'icon-warning-sign',
-    'FAILED': 'icon-exclamation-sign failed',
-    'HOLDING_FAILED': 'icon-exclamation-sign failed',
-    'SKIPPED_FAILED': 'icon-share-alt failed',
-    'PENDING': 'icon-cog pending',
-    'QUEUED': 'icon-cog queued',
-    'IN_PROGRESS': 'icon-cogs in_progress',
-    'HOLDING': 'icon-pause',
-    'SUSPENDED': 'icon-pause',
-    'ABORTED': 'icon-minus aborted',
-    'TIMEDOUT': 'icon-time timedout',
-    'HOLDING_TIMEDOUT': 'icon-time timedout',
-    'SUBITEM_FAILED': 'icon-remove failed'
-  },
-
-  classNameBindings: ['iconClass'],
-  attributeBindings: ['data-original-title'],
-
-  didInsertElement: function () {
-    App.tooltip($(this.get('element')));
-  },
-
-  'data-original-title': function() {
-    return this.get('content').toCapital();
-  }.property('content'),
-
-  /**
-   * @type {string}
-   */
-  iconClass: function () {
-    return this.get('statusIconMap')[this.get('content')] || 'icon-question-sign';
-  }.property('content')
-}));
-
-/**
  * Ambari overrides the default date transformer.
  * This is done because of the non-standard data
  * sent. For example Nagios sends date as "12345678".

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 9761d11..aa10456 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -92,6 +92,10 @@ require('views/common/widget/gauge_widget_view');
 require('views/common/widget/number_widget_view');
 require('views/common/widget/heatmap_widget_view');
 require('views/common/assign_master_components_view');
+require('views/common/helpers/format_word_break_view');
+require('views/common/helpers/format_null_view');
+require('views/common/helpers/pluralize_view');
+require('views/common/helpers/status_icon_view');
 require('views/common/chosen_plugin');
 require('views/common/export_metrics_menu_view');
 require('views/login');

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/views/common/helpers/format_null_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/helpers/format_null_view.js b/ambari-web/app/views/common/helpers/format_null_view.js
new file mode 100644
index 0000000..fa3d8e4
--- /dev/null
+++ b/ambari-web/app/views/common/helpers/format_null_view.js
@@ -0,0 +1,30 @@
+/**
+ * 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');
+
+App.FormatNullView = Em.View.extend({
+  tagName: 'span',
+  template: Em.Handlebars.compile('{{view.result}}'),
+
+  result: function() {
+    var emptyValue = this.get('empty') ? this.get('empty') : Em.I18n.t('services.service.summary.notAvailable');
+    emptyValue = emptyValue.startsWith('t:') ? Em.I18n.t(emptyValue.substr(2, emptyValue.length)) : emptyValue;
+    return (this.get('content') || this.get('content') == 0) ? this.get('content') : emptyValue;
+  }.property('content')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/views/common/helpers/format_word_break_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/helpers/format_word_break_view.js b/ambari-web/app/views/common/helpers/format_word_break_view.js
new file mode 100644
index 0000000..d33692c
--- /dev/null
+++ b/ambari-web/app/views/common/helpers/format_word_break_view.js
@@ -0,0 +1,52 @@
+/**
+ * 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');
+
+App.FormatWordBreakView = Em.View.extend({
+
+  attributeBindings: ["data-original-title"],
+
+  tagName: 'span',
+
+  template: Em.Handlebars.compile('{{{view.result}}}'),
+
+  _splitLongSubStrings: function (str) {
+    var maxBlockLength = 20;
+    if (str.length <= maxBlockLength || str.contains('-') || str.contains(' ')) {
+      return str;
+    }
+    return str.split(/(?=[A-Z])/).join('<wbr />');
+  },
+
+  /**
+   * @type {string}
+   */
+  result: function() {
+    var content = this.get('content') || '';
+    var self = this;
+    ['.', '_'].forEach(function (delimiter) {
+      if (content.contains(delimiter)) {
+        content = content.split(delimiter).map(function (substr) {
+          return self._splitLongSubStrings(substr);
+        }).join(delimiter + '<wbr />');
+      }
+    });
+    return content.replace(/(<wbr \/>){2,}/, '<wbr />'); // no need for double <wbr />
+  }.property('content')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/views/common/helpers/pluralize_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/helpers/pluralize_view.js b/ambari-web/app/views/common/helpers/pluralize_view.js
new file mode 100644
index 0000000..550173c
--- /dev/null
+++ b/ambari-web/app/views/common/helpers/pluralize_view.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');
+
+App.PluralizeView = Em.View.extend({
+  tagName: 'span',
+  template: Em.Handlebars.compile('{{view.wordOut}}'),
+
+  wordOut: function() {
+    var count, singular, plural;
+    count = this.get('content');
+    singular = this.get('singular');
+    plural = this.get('plural');
+    return this.getWord(count, singular, plural);
+  }.property('content'),
+  /**
+   * Get computed word.
+   *
+   * @param {Number} count
+   * @param {String} singular
+   * @param {String} [plural]
+   * @return {String}
+   * @method getWord
+   */
+  getWord: function(count, singular, plural) {
+    singular = this.parseValue(singular);
+    // if plural not passed
+    if (!plural) plural = singular + 's';
+    else plural = this.parseValue(plural);
+    if (singular && plural) {
+      if (count == 1) {
+        return singular;
+      } else {
+        return plural;
+      }
+    }
+    return '';
+  },
+  /**
+   * Detect and return value from its instance.
+   *
+   * @param {String} value
+   * @return {*}
+   * @method parseValue
+   **/
+  parseValue: function(value) {
+    switch (value[0]) {
+      case '@':
+        value = this.getViewPropertyValue(value);
+        break;
+      case 't':
+        value = this.tDetect(value);
+        break;
+      default:
+        break;
+    }
+    return value;
+  },
+  /*
+   * Detect for Em.I18n.t reference call
+   * @params word {String}
+   * return {String}
+   */
+  tDetect: function(word) {
+    var splitted = word.split(':');
+    if (splitted.length > 1 && splitted[0] == 't') {
+      return Em.I18n.t(splitted[1]);
+    } else {
+      return splitted[0];
+    }
+  },
+  /**
+   * Get property value from view|controller by its key path.
+   *
+   * @param {String} value - key path
+   * @return {*}
+   * @method getViewPropertyValue
+   **/
+  getViewPropertyValue: function(value) {
+    value = value.substr(1);
+    var keyword = value.split('.')[0]; // return 'controller' or 'view'
+    switch (keyword) {
+      case 'controller':
+        return Em.get(this, value);
+      case 'view':
+        return Em.get(this, value.replace(/^view/, 'parentView'));
+      default:
+        break;
+    }
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/app/views/common/helpers/status_icon_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/helpers/status_icon_view.js b/ambari-web/app/views/common/helpers/status_icon_view.js
new file mode 100644
index 0000000..70f56a9
--- /dev/null
+++ b/ambari-web/app/views/common/helpers/status_icon_view.js
@@ -0,0 +1,62 @@
+/**
+ * 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');
+
+App.StatusIconView = Em.View.extend({
+  tagName: 'i',
+
+  /**
+   * relation map between status and icon class
+   * @type {object}
+   */
+  statusIconMap: {
+    'COMPLETED': 'icon-ok completed',
+    'WARNING': 'icon-warning-sign',
+    'FAILED': 'icon-exclamation-sign failed',
+    'HOLDING_FAILED': 'icon-exclamation-sign failed',
+    'SKIPPED_FAILED': 'icon-share-alt failed',
+    'PENDING': 'icon-cog pending',
+    'QUEUED': 'icon-cog queued',
+    'IN_PROGRESS': 'icon-cogs in_progress',
+    'HOLDING': 'icon-pause',
+    'SUSPENDED': 'icon-pause',
+    'ABORTED': 'icon-minus aborted',
+    'TIMEDOUT': 'icon-time timedout',
+    'HOLDING_TIMEDOUT': 'icon-time timedout',
+    'SUBITEM_FAILED': 'icon-remove failed'
+  },
+
+  classNameBindings: ['iconClass'],
+  attributeBindings: ['data-original-title'],
+
+  didInsertElement: function () {
+    App.tooltip($(this.get('element')));
+  },
+
+  'data-original-title': function() {
+    return this.get('content').toCapital();
+  }.property('content'),
+
+  /**
+   * @type {string}
+   */
+  iconClass: function () {
+    return this.get('statusIconMap')[this.get('content')] || 'icon-question-sign';
+  }.property('content')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/1158f934/ambari-web/test/views/common/helpers/format_word_break_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/common/helpers/format_word_break_view_test.js b/ambari-web/test/views/common/helpers/format_word_break_view_test.js
new file mode 100644
index 0000000..40154af
--- /dev/null
+++ b/ambari-web/test/views/common/helpers/format_word_break_view_test.js
@@ -0,0 +1,54 @@
+/**
+ * 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 view;
+
+describe('App.FormatWordBreakView', function () {
+
+  beforeEach(function () {
+    view = App.FormatWordBreakView.create({});
+  });
+
+  describe('#result', function () {
+
+    Em.A([
+      {content: 'abc', expected: 'abc'},
+      {content: 'a.bc', expected: 'a.<wbr />bc'},
+      {content: 'a.b.c', expected: 'a.<wbr />b.<wbr />c'},
+      {content: 'a.123456789A123456789B12345.c', expected: 'a.<wbr />123456789<wbr />A123456789<wbr />B12345.<wbr />c'},
+      {content: 'a_bc', expected: 'a_<wbr />bc'},
+      {content: 'a_b_c', expected: 'a_<wbr />b_<wbr />c'},
+      {content: 'a_123456789A123456789B12345_c', expected: 'a_<wbr />123456789<wbr />A123456789<wbr />B12345_<wbr />c'},
+      {content: 'a.123456789A123456789B12345_c', expected: 'a.<wbr />123456789<wbr />A123456789<wbr />B12345_<wbr />c'},
+      {content: 'a.123456789a123456789b12345_c', expected: 'a.<wbr />123456789a123456789b12345_<wbr />c'},
+      {content: 'a.123456789a-23456789b12345_c', expected: 'a.<wbr />123456789a-23456789b12345_<wbr />c'},
+      {content: 'a.123456789a 23456789b12345_c', expected: 'a.<wbr />123456789a 23456789b12345_<wbr />c'},
+      {content: 'a.123456789a123456789_c', expected: 'a.<wbr />123456789a123456789_<wbr />c'},
+    ]).forEach(function (test) {
+      var message = 'content: {0}, expected: {1}'.format(JSON.stringify(test.content), JSON.stringify(test.expected));
+      it(message, function () {
+        view.set('content', test.content);
+        expect(view.get('result')).to.be.equal(test.expected);
+      });
+    });
+
+  });
+
+});
\ No newline at end of file