瀏覽代碼

AMBARI-5543. Unit tests for steps 6. (onechiporenko)

Oleg Nechiporenko 11 年之前
父節點
當前提交
d9b91a7fc5

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

@@ -138,6 +138,7 @@ require('test/views/wizard/step1_view_test');
 require('test/views/wizard/step2_view_test');
 require('test/views/wizard/step2_view_test');
 require('test/views/wizard/step3_view_test');
 require('test/views/wizard/step3_view_test');
 require('test/views/wizard/step5_view_test');
 require('test/views/wizard/step5_view_test');
+require('test/views/wizard/step6_view_test');
 require('test/views/wizard/step9_view_test');
 require('test/views/wizard/step9_view_test');
 require('test/views/wizard/step10_view_test');
 require('test/views/wizard/step10_view_test');
 require('test/models/host_test');
 require('test/models/host_test');

+ 106 - 45
ambari-web/app/controllers/wizard/step6_controller.js

@@ -33,7 +33,12 @@ var db = require('utils/db');
  */
  */
 App.WizardStep6Controller = Em.Controller.extend({
 App.WizardStep6Controller = Em.Controller.extend({
 
 
+  /**
+   * List of hosts
+   * @type {object[]}
+   */
   hosts: [],
   hosts: [],
+
   /**
   /**
    * List of components info about selecting/deselecting status for components.
    * List of components info about selecting/deselecting status for components.
    *
    *
@@ -41,33 +46,50 @@ App.WizardStep6Controller = Em.Controller.extend({
    *  @item {Em.Object}
    *  @item {Em.Object}
    *    @property name {String} - component name
    *    @property name {String} - component name
    *    @property label {String} - component display name
    *    @property label {String} - component display name
-   *    @property allChecked {Boolean} - all checkboxes are checked
-   *    @property noChecked {Boolean} - no checkboxes checked
+   *    @property allChecked {bool} - all checkboxes are checked
+   *    @property noChecked {bool} - no checkboxes checked
    */
    */
   headers: [],
   headers: [],
 
 
   /**
   /**
    * true - assign ZK, HB
    * true - assign ZK, HB
    * false - slaves and clients
    * false - slaves and clients
+   * @type {bool}
    */
    */
   isMasters: false,
   isMasters: false,
-  isLoaded: false,
 
 
+  /**
+   * @type {bool}
+   */
+  isLoaded: false,
 
 
+  /**
+   * Check if <code>addHostWizard</code> used
+   * @type {bool}
+   */
   isAddHostWizard: function () {
   isAddHostWizard: function () {
     return this.get('content.controllerName') === 'addHostController';
     return this.get('content.controllerName') === 'addHostController';
   }.property('content.controllerName'),
   }.property('content.controllerName'),
 
 
+  /**
+   * Check if <code>installerWizard</code> used
+   * @type {bool}
+   */
   isInstallerWizard: function () {
   isInstallerWizard: function () {
     return this.get('content.controllerName') === 'installerController';
     return this.get('content.controllerName') === 'installerController';
   }.property('content.controllerName'),
   }.property('content.controllerName'),
 
 
-  isAddServiceWizard: function() {
+  /**
+   * Check if <code>addHerviceWizard</code> used
+   * @type {bool}
+   */
+  isAddServiceWizard: function () {
     return this.get('content.controllerName') === 'addServiceController';
     return this.get('content.controllerName') === 'addServiceController';
   }.property('content.controllerName'),
   }.property('content.controllerName'),
 
 
   /**
   /**
-   * verify condition that at least one checkbox of each component was checked
+   * Verify condition that at least one checkbox of each component was checked
+   * @method clearError
    */
    */
   clearError: function () {
   clearError: function () {
     var self = this;
     var self = this;
@@ -114,6 +136,10 @@ App.WizardStep6Controller = Em.Controller.extend({
     }
     }
   },
   },
 
 
+  /**
+   * Clear Step6 data like <code>hosts</code>, <code>headers</code> etc
+   * @method clearStep
+   */
   clearStep: function () {
   clearStep: function () {
     this.set('hosts', []);
     this.set('hosts', []);
     this.set('headers', []);
     this.set('headers', []);
@@ -123,24 +149,33 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Enable some service for all hosts
    * Enable some service for all hosts
-   * @param event
+   * @param {object} event
+   * @method selectAllNodes
    */
    */
   selectAllNodes: function (event) {
   selectAllNodes: function (event) {
-    this.setAllNodes(event.context.name, true);
+    var name = Em.get(event, 'context.name');
+    if (name) {
+      this.setAllNodes(name, true);
+    }
   },
   },
 
 
   /**
   /**
    * Disable some services for all hosts
    * Disable some services for all hosts
-   * @param event
+   * @param {object} event
+   * @method deselectAllNodes
    */
    */
   deselectAllNodes: function (event) {
   deselectAllNodes: function (event) {
-    this.setAllNodes(event.context.name, false);
+    var name = Em.get(event, 'context.name');
+    if (name) {
+      this.setAllNodes(name, false);
+    }
   },
   },
 
 
   /**
   /**
    * Enable/disable some service for all hosts
    * Enable/disable some service for all hosts
    * @param {String} component - component name
    * @param {String} component - component name
-   * @param {Boolean} checked - true - enable, false - disable
+   * @param {bool} checked - true - enable, false - disable
+   * @method setAllNodes
    */
    */
   setAllNodes: function (component, checked) {
   setAllNodes: function (component, checked) {
     this.get('hosts').forEach(function (host) {
     this.get('hosts').forEach(function (host) {
@@ -155,8 +190,9 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Return whether service was selected or not
    * Return whether service was selected or not
-   * @param name serviceName
-   * @return {*}
+   * @param {string} name serviceName
+   * @return {bool}
+   * @method isServiceSelected
    */
    */
   isServiceSelected: function (name) {
   isServiceSelected: function (name) {
     return !!(this.get('content.services').findProperty('serviceName', name) &&
     return !!(this.get('content.services').findProperty('serviceName', name) &&
@@ -165,30 +201,44 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Checkbox check callback
    * Checkbox check callback
+   * Verify if all/none checkboxes for current component are checked
    * @param {String} component
    * @param {String} component
+   * @method checkCallback
    */
    */
   checkCallback: function (component) {
   checkCallback: function (component) {
     var header = this.get('headers').findProperty('name', component);
     var header = this.get('headers').findProperty('name', component);
-    var hosts = this.get('hosts');
-    var allTrue = true;
-    var allFalse = true;
-    hosts.forEach(function (host) {
-      host.get('checkboxes').forEach(function (checkbox) {
-        if (checkbox.get('component') === component && !checkbox.get('isInstalled')) {
-          allTrue &= checkbox.get('checked');
-          allFalse &= !checkbox.get('checked');
-        }
+    if (header) {
+      var hosts = this.get('hosts');
+      var allTrue = true;
+      var allFalse = true;
+      hosts.forEach(function (host) {
+        host.get('checkboxes').forEach(function (checkbox) {
+          if (checkbox.get('component') === component && !checkbox.get('isInstalled')) {
+            allTrue = allTrue && checkbox.get('checked');
+            allFalse = allFalse && !checkbox.get('checked');
+          }
+        });
       });
       });
-    });
-    header.set('allChecked', allTrue);
-    header.set('noChecked', allFalse);
+      header.set('allChecked', allTrue);
+      header.set('noChecked', allFalse);
+    }
     this.clearError();
     this.clearError();
   },
   },
 
 
+  /**
+   * Get <code>displayName</code> for component by <code>componentName</code>
+   * @param componentName
+   * @returns {string}
+   * @method getComponentDisplayName
+   */
   getComponentDisplayName: function (componentName) {
   getComponentDisplayName: function (componentName) {
-    return App.StackServiceComponent.find().findProperty('componentName', componentName).get('displayName')
+    return App.StackServiceComponent.find().findProperty('componentName', componentName).get('displayName');
   },
   },
 
 
+  /**
+   * Init step6 data
+   * @method loadStep
+   */
   loadStep: function () {
   loadStep: function () {
 
 
     var self = this;
     var self = this;
@@ -196,7 +246,7 @@ App.WizardStep6Controller = Em.Controller.extend({
     console.log("WizardStep6Controller: Loading step6: Assign Slaves");
     console.log("WizardStep6Controller: Loading step6: Assign Slaves");
     this.clearStep();
     this.clearStep();
 
 
-    var headers = [];
+    var headers = Em.A([]);
 
 
     if (this.get('isMasters')) {
     if (this.get('isMasters')) {
       if (this.isServiceSelected('HBASE') && App.supports.multipleHBaseMasters) {
       if (this.isServiceSelected('HBASE') && App.supports.multipleHBaseMasters) {
@@ -214,7 +264,7 @@ App.WizardStep6Controller = Em.Controller.extend({
     }
     }
     else {
     else {
       if (this.isServiceSelected('HDFS')) {
       if (this.isServiceSelected('HDFS')) {
-        headers.pushObject(Ember.Object.create({
+        headers.pushObject(Em.Object.create({
           name: 'DATANODE',
           name: 'DATANODE',
           label: self.getComponentDisplayName('DATANODE')
           label: self.getComponentDisplayName('DATANODE')
         }));
         }));
@@ -249,7 +299,7 @@ App.WizardStep6Controller = Em.Controller.extend({
           label: self.getComponentDisplayName('FLUME_HANDLER')
           label: self.getComponentDisplayName('FLUME_HANDLER')
         }));
         }));
       }
       }
-      headers.pushObject(Ember.Object.create({
+      headers.pushObject(Em.Object.create({
         name: 'CLIENT',
         name: 'CLIENT',
         label: App.format.role('CLIENT')
         label: App.format.role('CLIENT')
       }));
       }));
@@ -276,14 +326,17 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Get active host names
    * Get active host names
-   * @return {Array}
+   * @return {string[]}
+   * @method getHostNames
    */
    */
   getHostNames: function () {
   getHostNames: function () {
     var hostInfo = this.get('content.hosts');
     var hostInfo = this.get('content.hosts');
     var hostNames = [];
     var hostNames = [];
     for (var index in hostInfo) {
     for (var index in hostInfo) {
-      if (hostInfo[index].bootStatus === 'REGISTERED') {
-        hostNames.push(hostInfo[index].name);
+      if (hostInfo.hasOwnProperty(index)) {
+        if (hostInfo[index].bootStatus === 'REGISTERED') {
+          hostNames.push(hostInfo[index].name);
+        }
       }
       }
     }
     }
     return hostNames;
     return hostNames;
@@ -291,12 +344,13 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Load all data needed for this module. Then it automatically renders in template
    * Load all data needed for this module. Then it automatically renders in template
+   * @method render
    */
    */
   render: function () {
   render: function () {
-    var hostsObj = [];
-    var masterHosts = [];
-    var headers = this.get('headers');
-    var masterHostNames = this.get('content.masterComponentHosts').mapProperty('hostName').uniq();
+    var hostsObj = [],
+      masterHosts = [],
+      headers = this.get('headers'),
+      masterHostNames = this.get('content.masterComponentHosts').mapProperty('hostName').uniq();
 
 
     this.getHostNames().forEach(function (_hostName) {
     this.getHostNames().forEach(function (_hostName) {
       var hasMaster = masterHostNames.contains(_hostName);
       var hasMaster = masterHostNames.contains(_hostName);
@@ -339,9 +393,10 @@ App.WizardStep6Controller = Em.Controller.extend({
   },
   },
 
 
   /**
   /**
-   *
+   * Set checked values for slaves checkboxes
    * @param {Array} hostsObj
    * @param {Array} hostsObj
    * @return {Array}
    * @return {Array}
+   * @method renderSlaves
    */
    */
   renderSlaves: function (hostsObj) {
   renderSlaves: function (hostsObj) {
     var self = this;
     var self = this;
@@ -389,10 +444,11 @@ App.WizardStep6Controller = Em.Controller.extend({
   },
   },
 
 
   /**
   /**
-   * select checkboxes which correspond to master components
+   * Select checkboxes which correspond to master components
    *
    *
    * @param {Array} hostsObj
    * @param {Array} hostsObj
    * @return {Array}
    * @return {Array}
+   * @method selectMasterComponents
    */
    */
   selectMasterComponents: function (hostsObj) {
   selectMasterComponents: function (hostsObj) {
     var masterComponentHosts = this.get('content.masterComponentHosts');
     var masterComponentHosts = this.get('content.masterComponentHosts');
@@ -414,8 +470,9 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Return list of master components for specified <code>hostname</code>
    * Return list of master components for specified <code>hostname</code>
-   * @param hostName
-   * @return {*}
+   * @param {string} hostName
+   * @return {string[]}
+   * @method getMasterComponentsForHost
    */
    */
   getMasterComponentsForHost: function (hostName) {
   getMasterComponentsForHost: function (hostName) {
     return this.get('content.masterComponentHosts').filterProperty('hostName', hostName).mapProperty('component');
     return this.get('content.masterComponentHosts').filterProperty('hostName', hostName).mapProperty('component');
@@ -424,7 +481,8 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Validate form. Return do we have errors or not
    * Validate form. Return do we have errors or not
-   * @return {Boolean}
+   * @return {bool}
+   * @method validate
    */
    */
   validate: function () {
   validate: function () {
 
 
@@ -433,19 +491,21 @@ App.WizardStep6Controller = Em.Controller.extend({
     }
     }
     else {
     else {
       if (this.get('isInstallerWizard')) {
       if (this.get('isInstallerWizard')) {
-       return this.validateEachComponent() && this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForSlaveHost'));
+        return this.validateEachComponent() && this.validateEachHost(Em.I18n.t('installer.step6.error.mustSelectOneForSlaveHost'));
       }
       }
       else {
       else {
-        if(this.get('isAddServiceWizard')) {
+        if (this.get('isAddServiceWizard')) {
           return this.validateEachComponent();
           return this.validateEachComponent();
         }
         }
+        return true;
       }
       }
     }
     }
   },
   },
 
 
   /**
   /**
    * Validate all components for each host. Return do we have errors or not
    * Validate all components for each host. Return do we have errors or not
-   * @return {Boolean}
+   * @return {bool}
+   * @method validateEachHost
    */
    */
   validateEachHost: function (errorMsg) {
   validateEachHost: function (errorMsg) {
 
 
@@ -459,7 +519,7 @@ App.WizardStep6Controller = Em.Controller.extend({
       var checkboxes = hosts[i].get('checkboxes');
       var checkboxes = hosts[i].get('checkboxes');
       isError = false;
       isError = false;
       headers.forEach(function (header) {
       headers.forEach(function (header) {
-        isError |= checkboxes.findProperty('title', header.get('label')).checked;
+        isError = isError || checkboxes.findProperty('title', header.get('label')).checked;
       });
       });
       isError = !isError;
       isError = !isError;
       if (isError) {
       if (isError) {
@@ -472,7 +532,8 @@ App.WizardStep6Controller = Em.Controller.extend({
 
 
   /**
   /**
    * Validate a component for all hosts. Return do we have errors or not
    * Validate a component for all hosts. Return do we have errors or not
-   * @return {Boolean}
+   * @return {bool}
+   * @method validateEachComponent
    */
    */
   validateEachComponent: function () {
   validateEachComponent: function () {
     var isError = false;
     var isError = false;

+ 35 - 35
ambari-web/app/templates/wizard/step6.hbs

@@ -30,56 +30,56 @@
           <th>{{t common.host}}</th>
           <th>{{t common.host}}</th>
           {{#each header in controller.headers}}
           {{#each header in controller.headers}}
 
 
-              <th>
-                <a href="#" {{bindAttr class="header.allChecked:selected:deselected"}}
-                  {{action "selectAllNodes" header target="controller"}}>{{t all}}</a> |
+            <th>
+              <a href="#" {{bindAttr class="header.allChecked:selected:deselected"}}
+                {{action "selectAllNodes" header target="controller"}}>{{t all}}</a> |
 
 
-                <a href="#" {{bindAttr class="header.noChecked:selected:deselected"}}
-                  {{action "deselectAllNodes" header target="controller"}}>{{t none}}</a>
-              </th>
+              <a href="#" {{bindAttr class="header.noChecked:selected:deselected"}}
+                {{action "deselectAllNodes" header target="controller"}}>{{t none}}</a>
+            </th>
 
 
           {{/each}}
           {{/each}}
         </tr>
         </tr>
       </thead>
       </thead>
       <tbody>
       <tbody>
-      {{#if view.pageContent}}
-        {{#each host in view.pageContent}}
-          <tr>
-            {{#view App.WizardStep6HostView hostBinding="host" }}
-              {{host.hostName}}
-              {{#if host.hasMaster}}
-                <i class=icon-asterisks>&#10037;</i>
-              {{/if}}
-            {{/view}}
-            {{#each checkbox in host.checkboxes}}
-              <td>
+        {{#if view.pageContent}}
+          {{#each host in view.pageContent}}
+            <tr>
+              {{#view App.WizardStep6HostView hostBinding="host" }}
+                {{host.hostName}}
+                {{#if host.hasMaster}}
+                  <i class=icon-asterisks>&#10037;</i>
+                {{/if}}
+              {{/view}}
+              {{#each checkbox in host.checkboxes}}
+                <td>
                   <label class="checkbox">
                   <label class="checkbox">
                     {{#view view.checkboxView checkboxBinding="checkbox"}}
                     {{#view view.checkboxView checkboxBinding="checkbox"}}
                       {{checkbox.title}}
                       {{checkbox.title}}
                     {{/view}}
                     {{/view}}
                   </label>
                   </label>
-              </td>
-            {{/each}}
-          </tr>
-        {{/each}}
-      {{/if}}
+                </td>
+              {{/each}}
+            </tr>
+          {{/each}}
+        {{/if}}
       </tbody>
       </tbody>
     </table>
     </table>
   </div>
   </div>
-    <div id="hosts">
-        <div class="page-bar">
-            <div class="items-on-page">
-                <label>{{t common.show}}: {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
-            </div>
-            <div class="info">{{view.paginationInfo}}</div>
-            <div class="paging_two_button">
-              {{view view.paginationFirst}}
-              {{view view.paginationLeft}}
-              {{view view.paginationRight}}
-              {{view view.paginationLast}}
-            </div>
-        </div>
+  <div id="hosts">
+    <div class="page-bar">
+      <div class="items-on-page">
+        <label>{{t common.show}}: {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
+      </div>
+      <div class="info">{{view.paginationInfo}}</div>
+      <div class="paging_two_button">
+        {{view view.paginationFirst}}
+        {{view view.paginationLeft}}
+        {{view view.paginationRight}}
+        {{view view.paginationLast}}
+      </div>
     </div>
     </div>
+  </div>
   <div class="btn-area">
   <div class="btn-area">
     <a class="btn" {{action back}}>&larr; {{t common.back}}</a>
     <a class="btn" {{action back}}>&larr; {{t common.back}}</a>
     <a class="btn btn-success pull-right" {{action next}}>{{t common.next}} &rarr;</a>
     <a class="btn btn-success pull-right" {{action next}}>{{t common.next}} &rarr;</a>

+ 54 - 17
ambari-web/app/views/wizard/step6_view.js

@@ -25,16 +25,32 @@ App.WizardStep6View = App.TableView.extend({
 
 
   title: '',
   title: '',
 
 
+  /**
+   * Numbe rof visible rows
+   * @type {string}
+   */
   displayLength: "25",
   displayLength: "25",
 
 
+  /**
+   * List of hosts
+   * @type {object[]}
+   */
   content: function () {
   content: function () {
     return this.get('controller.hosts');
     return this.get('controller.hosts');
   }.property('controller.hosts'),
   }.property('controller.hosts'),
 
 
+  /**
+   * Synonym to <code>content</code> in this <code>App.TableView</code>
+   * @type {object[]}
+   */
   filteredContent: function () {
   filteredContent: function () {
     return this.get('content');
     return this.get('content');
   }.property('content'),
   }.property('content'),
 
 
+  /**
+   * Set <code>label</code>, <code>title</code> and do <code>loadStep</code>
+   * @method didInsertElement
+   */
   didInsertElement: function () {
   didInsertElement: function () {
     var controller = this.get('controller');
     var controller = this.get('controller');
     if (controller.get('isMasters')) {
     if (controller.get('isMasters')) {
@@ -49,6 +65,10 @@ App.WizardStep6View = App.TableView.extend({
     controller.loadStep();
     controller.loadStep();
   },
   },
 
 
+  /**
+   * Set <code>label</code> value
+   * @method setLabel
+   */
   setLabel: function () {
   setLabel: function () {
     var label = Em.I18n.t('installer.step6.body');
     var label = Em.I18n.t('installer.step6.body');
     var clients = this.get('controller.content.clients');
     var clients = this.get('controller.content.clients');
@@ -56,26 +76,30 @@ App.WizardStep6View = App.TableView.extend({
       if (clients.length === 1) {
       if (clients.length === 1) {
         label = label + ' ' + _client.display_name;
         label = label + ' ' + _client.display_name;
       }
       }
-      else
+      else {
         if (_client !== clients[clients.length - 1]) {           // [clients.length - 1]
         if (_client !== clients[clients.length - 1]) {           // [clients.length - 1]
           label = label + ' ' + _client.display_name;
           label = label + ' ' + _client.display_name;
-          if(_client !== clients[clients.length - 2]) {
+          if (_client !== clients[clients.length - 2]) {
             label = label + ',';
             label = label + ',';
           }
           }
         }
         }
         else {
         else {
           label = label + ' ' + Em.I18n.t('and') + ' ' + _client.display_name + '.';
           label = label + ' ' + Em.I18n.t('and') + ' ' + _client.display_name + '.';
         }
         }
+      }
     }, this);
     }, this);
     this.set('label', label);
     this.set('label', label);
   },
   },
+
   /**
   /**
    * Binding host property with dynamic name
    * Binding host property with dynamic name
-   * @type {*}
+   * @type {Ember.Checkbox}
    */
    */
   checkboxView: Em.Checkbox.extend({
   checkboxView: Em.Checkbox.extend({
+
     /**
     /**
      * Header object with host property name
      * Header object with host property name
+     * @type {object}
      */
      */
     checkbox: null,
     checkbox: null,
 
 
@@ -85,6 +109,10 @@ App.WizardStep6View = App.TableView.extend({
 
 
     disabledBinding: 'checkbox.isInstalled',
     disabledBinding: 'checkbox.isInstalled',
 
 
+    /**
+     * Click-handler on checkbox
+     * @method click
+     */
     click: function () {
     click: function () {
       if ($.browser.mozilla) {
       if ($.browser.mozilla) {
         this.toggleProperty('checkbox.checked');
         this.toggleProperty('checkbox.checked');
@@ -96,24 +124,33 @@ App.WizardStep6View = App.TableView.extend({
 
 
 App.WizardStep6HostView = Em.View.extend({
 App.WizardStep6HostView = Em.View.extend({
 
 
+  /**
+   * Binded <code>host</code> object
+   * @type {object}
+   */
   host: null,
   host: null,
+
   tagName: 'td',
   tagName: 'td',
 
 
+  /**
+   * Create hover-labels for hostName with list of installed master-components
+   * @method didInsertElement
+   */
   didInsertElement: function () {
   didInsertElement: function () {
-    if (!this.get('controller.isMasters')) {
-      var components = this.get('controller').getMasterComponentsForHost(this.get('host.hostName'));
-      if (components && components.length > 0) {
-        components = components.map(function(_component) {
-          return App.format.role(_component);
-        });
-        components = components.join(" /\n");
-        App.popover(this.$(), {
-          title: Em.I18n.t('installer.step6.wizardStep6Host.title').format(this.get('host.hostName')),
-          content: components,
-          placement: 'right',
-          trigger: 'hover'
-        });
-      }
+    if(this.get('controller.isMasters')) return;
+
+    var components = this.get('controller').getMasterComponentsForHost(this.get('host.hostName'));
+    if (components && components.length > 0) {
+      components = components.map(function (_component) {
+        return App.format.role(_component);
+      });
+      components = components.join(" /\n");
+      App.popover(this.$(), {
+        title: Em.I18n.t('installer.step6.wizardStep6Host.title').format(this.get('host.hostName')),
+        content: components,
+        placement: 'right',
+        trigger: 'hover'
+      });
     }
     }
   }
   }
 });
 });

+ 1226 - 84
ambari-web/test/installer/step6_test.js

@@ -18,101 +18,392 @@
 
 
 var Ember = require('ember');
 var Ember = require('ember');
 var App = require('app');
 var App = require('app');
+require('utils/helper');
 require('controllers/wizard/step6_controller');
 require('controllers/wizard/step6_controller');
-
+var controller,
+  services = [
+    Em.Object.create({
+      serviceName: 'MAPREDUCE',
+      isSelected: true
+    }),
+    Em.Object.create({
+      serviceName: 'YARN',
+      isSelected: true
+    }),
+    Em.Object.create({
+      serviceName: 'HBASE',
+      isSelected: true
+    }),
+    Em.Object.create({
+      serviceName: 'HDFS',
+      isSelected: true
+    }),
+    Em.Object.create({
+      serviceName: 'STORM',
+      isSelected: true
+    }),
+    Em.Object.create({
+      serviceName: 'FLUME',
+      isSelected: true
+    })
+  ];
 describe('App.WizardStep6Controller', function () {
 describe('App.WizardStep6Controller', function () {
 
 
-  var controller = App.WizardStep6Controller.create();
-  controller.set('content', {
-    hosts: {},
-    masterComponentHosts: {},
-    services: [
-      Em.Object.create({
-        serviceName: 'MAPREDUCE',
-        isSelected: true
-      }),
-      Em.Object.create({
-        serviceName: 'YARN',
-        isSelected: true
-      }),
-      Em.Object.create({
-        serviceName: 'HBASE',
-        isSelected: true
-      }),
-      Em.Object.create({
-        serviceName: 'HDFS',
-        isSelected: true
-      })
-    ]
-  });
-  controller.set('getComponentDisplayName',function () {
-    return true;
-  });
-
-  var HOSTS = Em.A([ 'host0', 'host1', 'host2', 'host3' ]);
+  beforeEach(function () {
+    controller = App.WizardStep6Controller.create();
+    controller.set('content', {
+      hosts: {},
+      masterComponentHosts: {},
+      services: services
+    });
+    sinon.stub(controller, 'getComponentDisplayName', function (c) {
+      return App.format.components[c];
+    });
 
 
-  var h = {};
-  var m = [];
-  HOSTS.forEach(function (hostName) {
-    var obj = Em.Object.create({
-      name: hostName,
-      hostName: hostName,
-      bootStatus: 'REGISTERED'
+    var h = {}, m = [];
+    Em.A(['host0', 'host1', 'host2', 'host3']).forEach(function (hostName) {
+      var obj = Em.Object.create({
+        name: hostName,
+        hostName: hostName,
+        bootStatus: 'REGISTERED'
+      });
+      h[hostName] = obj;
+      m.push(obj);
     });
     });
-    h[hostName] = obj;
-    m.push(obj);
+
+    controller.set('content.hosts', h);
+    controller.set('content.masterComponentHosts', m);
+    controller.set('isMasters', false);
+
   });
   });
 
 
-  controller.set('content.hosts', h);
-  controller.set('content.masterComponentHosts', m);
-  controller.set('isMasters', false);
+  afterEach(function () {
+    controller.getComponentDisplayName.restore();
+  });
 
 
+  describe('#loadStep', function () {
+    Em.A([
+        {
+          isMasters: false,
+          services: [
+            Em.Object.create({
+              serviceName: 'MAPREDUCE',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 2,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: false,
+          services: [
+            Em.Object.create({
+              serviceName: 'MAPREDUCE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'YARN',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 3,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: false,
+          services: [
+            Em.Object.create({
+              serviceName: 'MAPREDUCE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'YARN',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'HBASE',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 4,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: false,
+          services: [
+            Em.Object.create({
+              serviceName: 'MAPREDUCE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'YARN',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'HBASE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'HDFS',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 5,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: false,
+          services: [
+            Em.Object.create({
+              serviceName: 'MAPREDUCE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'YARN',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'HBASE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'HDFS',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'STORM',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 6,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: false,
+          services: [
+            Em.Object.create({
+              serviceName: 'MAPREDUCE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'YARN',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'HBASE',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'HDFS',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'STORM',
+              isSelected: true
+            }),
+            Em.Object.create({
+              serviceName: 'FLUME',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 7,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: true,
+          multipleHBaseMasters: true,
+          services: [
+            Em.Object.create({
+              serviceName: 'HBASE',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 1,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: true,
+          multipleHBaseMasters: false,
+          services: [
+            Em.Object.create({
+              serviceName: 'HBASE',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 0,
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          isMasters: true,
+          services: [
+            Em.Object.create({
+              serviceName: 'ZOOKEEPER',
+              isSelected: true
+            })
+          ],
+          e: {
+            l: 1,
+            allChecked: false,
+            noChecked: true
+          }
+        }
+      ]).forEach(function (test) {
+        it(test.isMasters.toString() + ' ' + test.services.mapProperty('serviceName').join(', '), function () {
+          if (test.hasOwnProperty('multipleHBaseMasters')) {
+            App.set('supports.multipleHBaseMasters', test.multipleHBaseMasters);
+          }
+          controller.set('content.services', test.services);
+          controller.set('isMasters', test.isMasters);
+          sinon.stub(controller, 'render', Em.K);
+          controller.loadStep();
+          expect(controller.get('headers.length')).to.equal(test.e.l);
+          expect(controller.get('headers').everyProperty('allChecked', test.e.allChecked)).to.equal(true);
+          expect(controller.get('headers').everyProperty('noChecked', test.e.noChecked)).to.equal(true);
+          controller.clearStep();
+          controller.render.restore();
+        });
+      });
 
 
-  describe('#loadStep', function() {
-    controller.loadStep();
-    it('Hosts are loaded', function() {
-      expect(controller.get('hosts').length).to.equal(HOSTS.length);
-    });
+    Em.A([
+        {
+          p: {
+            isMasters: true,
+            skipMasterStep: true
+          },
+          e: true
+        },
+        {
+          p: {
+            isMasters: false,
+            skipMasterStep: true
+          },
+          e: false
+        },
+        {
+          p: {
+            isMasters: true,
+            skipMasterStep: false
+          },
+          e: false
+        },
+        {
+          p: {
+            isMasters: false,
+            skipMasterStep: false
+          },
+          e: false
+        }
+      ]).forEach(function (test) {
+        it('should skip this step if isMasters is ' + test.p.isMasters + ' and content.skipMasterStep is ' + test.p.skipMasterStep, function () {
+          controller.set('isMasters', test.p.isMasters);
+          controller.set('content.skipMasterStep', test.p.skipMasterStep);
+          sinon.stub(App.router, 'send', Em.K);
+          controller.loadStep();
+          expect(App.router.send.calledWith('next')).to.equal(test.e);
+          App.router.send.restore();
+        });
+      });
+
+    Em.A([
+        {
+          p: {
+            isMasters: true,
+            skipSlavesStep: true
+          },
+          e: false
+        },
+        {
+          p: {
+            isMasters: false,
+            skipSlavesStep: true
+          },
+          e: true
+        },
+        {
+          p: {
+            isMasters: true,
+            skipSlavesStep: false
+          },
+          e: false
+        },
+        {
+          p: {
+            isMasters: false,
+            skipSlavesStep: false
+          },
+          e: false
+        }
+      ]).forEach(function (test) {
+        it('should skip this step if isMasters is ' + test.p.isMasters + ' and content.skipSlavesStep is ' + test.p.skipSlavesStep, function () {
+          controller.set('isMasters', test.p.isMasters);
+          controller.set('content.skipSlavesStep', test.p.skipSlavesStep);
+          sinon.stub(App.router, 'send', Em.K);
+          controller.loadStep();
+          expect(App.router.send.calledWith('next')).to.equal(test.e);
+          App.router.send.restore();
+        });
+      });
 
 
-    it('Headers are loaded', function() {
-      expect(controller.get('headers').length).not.to.equal(0);
-    });
   });
   });
 
 
-  describe('#isAddHostWizard', function() {
-    it('true if content.controllerName is addHostController', function() {
+  describe('#isAddHostWizard', function () {
+    it('true if content.controllerName is addHostController', function () {
       controller.set('content.controllerName', 'addHostController');
       controller.set('content.controllerName', 'addHostController');
       expect(controller.get('isAddHostWizard')).to.equal(true);
       expect(controller.get('isAddHostWizard')).to.equal(true);
     });
     });
-    it('false if content.controllerName is not addHostController', function() {
+    it('false if content.controllerName is not addHostController', function () {
       controller.set('content.controllerName', 'mainController');
       controller.set('content.controllerName', 'mainController');
       expect(controller.get('isAddHostWizard')).to.equal(false);
       expect(controller.get('isAddHostWizard')).to.equal(false);
     });
     });
   });
   });
 
 
-  describe('#isInstallerWizard', function() {
-    it('true if content.controllerName is addHostController', function() {
+  describe('#isInstallerWizard', function () {
+    it('true if content.controllerName is addHostController', function () {
       controller.set('content.controllerName', 'installerController');
       controller.set('content.controllerName', 'installerController');
       expect(controller.get('isInstallerWizard')).to.equal(true);
       expect(controller.get('isInstallerWizard')).to.equal(true);
     });
     });
-    it('false if content.controllerName is not addHostController', function() {
+    it('false if content.controllerName is not addHostController', function () {
       controller.set('content.controllerName', 'mainController');
       controller.set('content.controllerName', 'mainController');
       expect(controller.get('isInstallerWizard')).to.equal(false);
       expect(controller.get('isInstallerWizard')).to.equal(false);
     });
     });
   });
   });
 
 
-  describe('#isAddServiceWizard', function() {
-    it('true if content.controllerName is addServiceController', function() {
+  describe('#isAddServiceWizard', function () {
+    it('true if content.controllerName is addServiceController', function () {
       controller.set('content.controllerName', 'addServiceController');
       controller.set('content.controllerName', 'addServiceController');
       expect(controller.get('isAddServiceWizard')).to.equal(true);
       expect(controller.get('isAddServiceWizard')).to.equal(true);
     });
     });
-    it('false if content.controllerName is not addServiceController', function() {
+    it('false if content.controllerName is not addServiceController', function () {
       controller.set('content.controllerName', 'mainController');
       controller.set('content.controllerName', 'mainController');
       expect(controller.get('isAddServiceWizard')).to.equal(false);
       expect(controller.get('isAddServiceWizard')).to.equal(false);
     });
     });
   });
   });
 
 
-  describe('#setAllNodes', function() {
+  describe('#setAllNodes', function () {
 
 
     var test_config = Em.A([
     var test_config = Em.A([
       {
       {
@@ -137,11 +428,12 @@ describe('App.WizardStep6Controller', function () {
       }
       }
     ]);
     ]);
 
 
-    test_config.forEach(function(test) {
-      it((test.state?'Select':'Deselect') + ' all ' + test.title, function() {
+    test_config.forEach(function (test) {
+      it((test.state ? 'Select' : 'Deselect') + ' all ' + test.title, function () {
+        controller.loadStep();
         controller.setAllNodes(test.name, test.state);
         controller.setAllNodes(test.name, test.state);
         var hosts = controller.get('hosts');
         var hosts = controller.get('hosts');
-        hosts.forEach(function(host) {
+        hosts.forEach(function (host) {
           var cb = host.get('checkboxes').filterProperty('isInstalled', false).findProperty('component', test.name);
           var cb = host.get('checkboxes').filterProperty('isInstalled', false).findProperty('component', test.name);
           if (cb) {
           if (cb) {
             expect(cb.get('checked')).to.equal(test.state);
             expect(cb.get('checked')).to.equal(test.state);
@@ -153,36 +445,41 @@ describe('App.WizardStep6Controller', function () {
 
 
   });
   });
 
 
-  describe('#isServiceSelected', function() {
-    controller.get('content.services').forEach(function(service) {
-      it(service.serviceName + ' is selected', function() {
-        expect(controller.isServiceSelected(service.serviceName)).to.equal(true);
+  describe('#isServiceSelected', function () {
+    describe('selected', function () {
+      services.forEach(function (service) {
+        it(service.serviceName + ' is selected', function () {
+          expect(controller.isServiceSelected(service.serviceName)).to.equal(true);
+        });
       });
       });
     });
     });
     var unselectedService = 'FAKESERVICE';
     var unselectedService = 'FAKESERVICE';
-    it(unselectedService + ' is not selected', function() {
+    it(unselectedService + ' is not selected', function () {
       expect(controller.isServiceSelected(unselectedService)).to.equal(false);
       expect(controller.isServiceSelected(unselectedService)).to.equal(false);
     });
     });
   });
   });
 
 
-  describe('#validateEachComponent', function() {
-    it('Nothing checked', function() {
-      controller.get('hosts').forEach(function(host) {
+  describe('#validateEachComponent', function () {
+    beforeEach(function () {
+      controller.loadStep();
+    });
+    it('Nothing checked', function () {
+      controller.get('hosts').forEach(function (host) {
         host.get('checkboxes').setEach('checked', false);
         host.get('checkboxes').setEach('checked', false);
       });
       });
       expect(controller.validateEachComponent('')).to.equal(false);
       expect(controller.validateEachComponent('')).to.equal(false);
     });
     });
-    it('One slave is not selected for no one host', function() {
-      controller.get('hosts').forEach(function(host) {
-        host.get('checkboxes').forEach(function(checkbox, index) {
+    it('One slave is not selected for no one host', function () {
+      controller.get('hosts').forEach(function (host) {
+        host.get('checkboxes').forEach(function (checkbox, index) {
           checkbox.set('checked', index === 0);
           checkbox.set('checked', index === 0);
         });
         });
       });
       });
       expect(controller.validateEachComponent('')).to.equal(false);
       expect(controller.validateEachComponent('')).to.equal(false);
     });
     });
-    it('All checked', function() {
-      controller.get('hosts').forEach(function(host) {
-        host.get('checkboxes').forEach(function(checkbox) {
+    it('All checked', function () {
+      controller.get('hosts').forEach(function (host) {
+        host.get('checkboxes').forEach(function (checkbox) {
           checkbox.set('checked', true);
           checkbox.set('checked', true);
         });
         });
       });
       });
@@ -190,25 +487,870 @@ describe('App.WizardStep6Controller', function () {
     });
     });
   });
   });
 
 
-  describe('#validateEachHost', function() {
-    it('Nothing checked', function() {
-      controller.get('hosts').forEach(function(host) {
+  describe('#validateEachHost', function () {
+    beforeEach(function () {
+      controller.loadStep();
+    });
+    it('Nothing checked', function () {
+      controller.get('hosts').forEach(function (host) {
         host.get('checkboxes').setEach('checked', false);
         host.get('checkboxes').setEach('checked', false);
       });
       });
       expect(controller.validateEachHost('')).to.equal(false);
       expect(controller.validateEachHost('')).to.equal(false);
     });
     });
-    it('One host doesn\'t have assigned slaves', function() {
-      controller.get('hosts').forEach(function(host, index) {
+    it('One host doesn\'t have assigned slaves', function () {
+      controller.get('hosts').forEach(function (host, index) {
         host.get('checkboxes').setEach('checked', index === 0);
         host.get('checkboxes').setEach('checked', index === 0);
       });
       });
       expect(controller.validateEachHost('')).to.equal(false);
       expect(controller.validateEachHost('')).to.equal(false);
     });
     });
-    it('All checked', function() {
-      controller.get('hosts').forEach(function(host) {
+    it('All checked', function () {
+      controller.get('hosts').forEach(function (host) {
         host.get('checkboxes').setEach('checked', true);
         host.get('checkboxes').setEach('checked', true);
       });
       });
       expect(controller.validateEachHost('')).to.equal(true);
       expect(controller.validateEachHost('')).to.equal(true);
     });
     });
   });
   });
 
 
-});
+  describe('#clearStep', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'clearError', Em.K);
+    });
+    afterEach(function () {
+      controller.clearError.restore();
+    });
+    it('should call clearError', function () {
+      controller.clearStep();
+      expect(controller.clearError.calledOnce).to.equal(true);
+    });
+    it('should clear hosts', function () {
+      controller.set('hosts', [
+        {},
+        {}
+      ]);
+      controller.clearStep();
+      expect(controller.get('hosts')).to.eql([]);
+    });
+    it('should clear headers', function () {
+      controller.set('headers', [
+        {},
+        {}
+      ]);
+      controller.clearStep();
+      expect(controller.get('headers')).to.eql([]);
+    });
+    it('should set isLoaded to false', function () {
+      controller.set('isLoaded', true);
+      controller.clearStep();
+      expect(controller.get('isLoaded')).to.equal(false);
+    });
+  });
+
+  describe('#selectAllNodes', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'setAllNodes', Em.K);
+    });
+    afterEach(function () {
+      controller.setAllNodes.restore();
+    });
+    it('should call setAllNodes', function () {
+      controller.selectAllNodes({context: {name: 'name'}});
+      expect(controller.setAllNodes.calledWith('name', true)).to.equal(true);
+    });
+    it('shouldn\'t call setAllNodes', function () {
+      controller.selectAllNodes();
+      expect(controller.setAllNodes.called).to.equal(false);
+    });
+  });
+
+  describe('#deselectAllNodes', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'setAllNodes', Em.K);
+    });
+    afterEach(function () {
+      controller.setAllNodes.restore();
+    });
+    it('should call setAllNodes', function () {
+      controller.deselectAllNodes({context: {name: 'name'}});
+      expect(controller.setAllNodes.calledWith('name', false)).to.equal(true);
+    });
+    it('shouldn\'t call setAllNodes', function () {
+      controller.deselectAllNodes();
+      expect(controller.setAllNodes.called).to.equal(false);
+    });
+  });
+
+  describe('#checkCallback', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'clearError', Em.K);
+    });
+    afterEach(function () {
+      controller.clearError.restore();
+    });
+    it('should call clearError', function () {
+      controller.checkCallback('');
+      expect(controller.clearError.calledOnce).to.equal(true);
+    });
+    Em.A([
+        {
+          m: 'all checked, isInstalled false',
+          headers: Em.A([
+            Em.Object.create({name: 'c1'})
+          ]),
+          hosts: Em.A([
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: false,
+                  checked: true
+                })
+              ])
+            })
+          ]),
+          component: 'c1',
+          e: {
+            allChecked: true,
+            noChecked: false
+          }
+        },
+        {
+          m: 'all checked, isInstalled true',
+          headers: Em.A([
+            Em.Object.create({name: 'c1'})
+          ]),
+          hosts: Em.A([
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: true,
+                  checked: true
+                })
+              ])
+            })
+          ]),
+          component: 'c1',
+          e: {
+            allChecked: true,
+            noChecked: true
+          }
+        },
+        {
+          m: 'no one checked',
+          headers: Em.A([
+            Em.Object.create({name: 'c1'})
+          ]),
+          hosts: Em.A([
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: false,
+                  checked: false
+                })
+              ])
+            })
+          ]),
+          component: 'c1',
+          e: {
+            allChecked: false,
+            noChecked: true
+          }
+        },
+        {
+          m: 'some checked',
+          headers: Em.A([
+            Em.Object.create({name: 'c1'})
+          ]),
+          hosts: Em.A([
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: false,
+                  checked: true
+                }),
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: false,
+                  checked: false
+                })
+              ])
+            })
+          ]),
+          component: 'c1',
+          e: {
+            allChecked: false,
+            noChecked: false
+          }
+        },
+        {
+          m: 'some checked, some isInstalled true',
+          headers: Em.A([
+            Em.Object.create({name: 'c1'})
+          ]),
+          hosts: Em.A([
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: true,
+                  checked: true
+                }),
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: true,
+                  checked: true
+                })
+              ])
+            })
+          ]),
+          component: 'c1',
+          e: {
+            allChecked: true,
+            noChecked: true
+          }
+        },
+        {
+          m: 'some checked, some isInstalled true (2)',
+          headers: Em.A([
+            Em.Object.create({name: 'c1'})
+          ]),
+          hosts: Em.A([
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: false,
+                  checked: false
+                }),
+                Em.Object.create({
+                  component: 'c1',
+                  isInstalled: true,
+                  checked: true
+                })
+              ])
+            })
+          ]),
+          component: 'c1',
+          e: {
+            allChecked: false,
+            noChecked: true
+          }
+        }
+      ]).forEach(function (test) {
+        it(test.m, function () {
+          controller.clearStep();
+          controller.set('headers', test.headers);
+          controller.set('hosts', test.hosts);
+          controller.checkCallback(test.component);
+          var header = controller.get('headers').findProperty('name', test.component);
+          expect(header.get('allChecked')).to.equal(test.e.allChecked);
+          expect(header.get('noChecked')).to.equal(test.e.noChecked);
+        });
+      });
+  });
+
+  describe('#getHostNames', function () {
+    var tests = Em.A([
+      {
+        hosts: {
+          h1: {bootStatus: 'REGISTERED', name: 'h1'},
+          h2: {bootStatus: 'REGISTERED', name: 'h2'},
+          h3: {bootStatus: 'REGISTERED', name: 'h3'}
+        },
+        m: 'All REGISTERED',
+        e: ['h1', 'h2', 'h3']
+      },
+      {
+        hosts: {
+          h1: {bootStatus: 'REGISTERED', name: 'h1'},
+          h2: {bootStatus: 'FAILED', name: 'h2'},
+          h3: {bootStatus: 'REGISTERED', name: 'h3'}
+        },
+        m: 'Some REGISTERED',
+        e: ['h1', 'h3']
+      },
+      {
+        hosts: {
+          h1: {bootStatus: 'FAILED', name: 'h1'},
+          h2: {bootStatus: 'FAILED', name: 'h2'},
+          h3: {bootStatus: 'FAILED', name: 'h3'}
+        },
+        m: 'No one REGISTERED',
+        e: []
+      },
+      {
+        hosts: {},
+        m: 'Empty hosts',
+        e: []
+      }
+    ]);
+    tests.forEach(function (test) {
+      it(test.m, function () {
+        controller.set('content.hosts', test.hosts);
+        var r = controller.getHostNames();
+        expect(r).to.eql(test.e);
+      });
+    });
+  });
+
+  describe('#validate', function () {
+    var tests = Em.A([
+      {
+        controllerName: 'addHostController',
+        method: 'validateEachHost',
+        r: true,
+        e: true
+      },
+      {
+        controllerName: 'addHostController',
+        method: 'validateEachHost',
+        r: false,
+        e: false
+      },
+      {
+        controllerName: 'addServiceController',
+        method: 'validateEachComponent',
+        r: true,
+        e: true
+      },
+      {
+        controllerName: 'addServiceController',
+        method: 'validateEachComponent',
+        r: false,
+        e: false
+      },
+      {
+        controllerName: 'installerController',
+        method: 'validateEachComponent',
+        r: true,
+        e: true
+      },
+      {
+        controllerName: 'installerController',
+        method: 'validateEachComponent',
+        r: false,
+        e: false
+      }
+    ]);
+    tests.forEach(function (test) {
+      it(test.controllerName + ' ' + test.method + ' returns ' + test.r.toString(), function () {
+        sinon.stub(controller, test.method, function () {
+          return test.r
+        });
+        controller.set('content.controllerName', test.controllerName);
+        expect(controller.validate()).to.equal(test.e);
+        controller[test.method].restore();
+      });
+    });
+  });
+
+  describe('#getMasterComponentsForHost', function () {
+    var tests = Em.A([
+      {
+        masterComponentHosts: Em.A([
+          {hostName: 'h1', component: 'c1'}
+        ]),
+        hostName: 'h1',
+        m: 'host exists',
+        e: ['c1']
+      },
+      {
+        masterComponentHosts: Em.A([
+          {hostName: 'h1', component: 'c1'}
+        ]),
+        hostName: 'h2',
+        m: 'host donesn\'t exists',
+        e: []
+      }
+    ]);
+    tests.forEach(function (test) {
+      it(test.m, function () {
+        controller.set('content.masterComponentHosts', test.masterComponentHosts);
+        var r = controller.getMasterComponentsForHost(test.hostName);
+        expect(r).to.eql(test.e);
+      });
+    });
+  });
+
+  describe('#selectMasterComponents', function () {
+    var tests = Em.A([
+      {
+        masterComponentHosts: Em.A([
+          {
+            hostName: 'h1',
+            component: 'c1'
+          }
+        ]),
+        hostsObj: [
+          Em.Object.create({
+            hostName: 'h1',
+            checkboxes: [
+              Em.Object.create({
+                component: 'c1',
+                checked: false
+              })
+            ]
+          })
+        ],
+        e: true,
+        m: 'host and component exist'
+      },
+      {
+        masterComponentHosts: Em.A([
+          {
+            hostName: 'h1',
+            component: 'c2'
+          }
+        ]),
+        hostsObj: [
+          Em.Object.create({
+            hostName: 'h1',
+            checkboxes: [
+              Em.Object.create({
+                component: 'c1',
+                checked: false
+              })
+            ]
+          })
+        ],
+        e: false,
+        m: 'host exists'
+      },
+      {
+        masterComponentHosts: Em.A([
+          {
+            hostName: 'h2',
+            component: 'c2'
+          }
+        ]),
+        hostsObj: [
+          Em.Object.create({
+            hostName: 'h1',
+            checkboxes: [
+              Em.Object.create({
+                component: 'c1',
+                checked: false
+              })
+            ]
+          })
+        ],
+        e: false,
+        m: 'host and component don\'t exist'
+      }
+    ]);
+    tests.forEach(function (test) {
+      it(test.m, function () {
+        controller.set('content.masterComponentHosts', test.masterComponentHosts);
+        var r = controller.selectMasterComponents(test.hostsObj);
+        expect(r.findProperty('hostName', 'h1').get('checkboxes').findProperty('component', 'c1').get('checked')).to.equal(test.e);
+      });
+    });
+  });
+
+  describe('#render', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'selectMasterComponents', Em.K);
+      sinon.stub(controller, 'renderSlaves', Em.K);
+    });
+    afterEach(function () {
+      controller.selectMasterComponents.restore();
+      controller.renderSlaves.restore();
+    });
+    it('should call selectMasterComponents if isMasters is true', function () {
+      controller.set('isMasters', true);
+      controller.render();
+      expect(controller.selectMasterComponents.calledOnce).to.equal(true);
+      expect(controller.renderSlaves.called).to.equal(false);
+    });
+    it('should call renderSlaves if isMasters is false', function () {
+      controller.set('isMasters', false);
+      controller.render();
+      expect(controller.selectMasterComponents.called).to.equal(false);
+      expect(controller.renderSlaves.calledOnce).to.equal(true);
+    });
+    it('should set isLoaded to true', function () {
+      controller.set('isLoaded', false);
+      controller.render();
+      expect(controller.get('isLoaded')).to.equal(true);
+    });
+  });
+
+  describe('#renderSlaves', function () {
+    Em.A([
+        {
+          controllerName: 'addServiceController',
+          slaveComponents: [],
+          hostsObj: [
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: ''}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: true
+            })
+          ],
+          m: 'host with masters, empty slaveComponents, controllerName - addServiceController',
+          e: [
+            [false, false, false]
+          ]
+        },
+        {
+          controllerName: 'addServiceController',
+          slaveComponents: [],
+          hostsObj: [
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: ''}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: false
+            })
+          ],
+          m: 'host without masters, empty slaveComponents, controllerName - addServiceController',
+          e: [
+            [false, true, true]
+          ]
+        },
+        {
+          controllerName: 'addServiceController',
+          slaveComponents: [],
+          services: [
+            Em.Object.create({serviceName: 'HDFS', isSelected: true})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: 'DataNode', checked: true}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: false
+            })
+          ],
+          m: 'host without masters, empty slaveComponents, controllerName - addServiceController, one datanode checked',
+          e: [
+            [true, true, true]
+          ]
+        },
+        {
+          controllerName: 'addServiceController',
+          slaveComponents: [],
+          services: [
+            Em.Object.create({serviceName: 'HDFS', isSelected: true})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: 'DataNode', checked: false}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: true
+            })
+          ],
+          m: 'host with masters, empty slaveComponents, controllerName - addServiceController, one datanode not checked',
+          e: [
+            [false, false, false]
+          ]
+        },
+        {
+          controllerName: 'installerController',
+          slaveComponents: [],
+          services: [
+            Em.Object.create({serviceName: 'HDFS', isSelected: true})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: 'DataNode', checked: true}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: true
+            })
+          ],
+          m: 'host with masters, empty slaveComponents, controllerName - installerController, one datanode checked',
+          e: [
+            [true, true, true]
+          ]
+        },
+        {
+          controllerName: 'installerController',
+          slaveComponents: [],
+          services: [
+            Em.Object.create({serviceName: 'HDFS', isSelected: true})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: 'DataNode', checked: false}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: true
+            }),
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: 'DataNode', checked: true}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: true
+            })
+          ],
+          m: 'hosts with masters, empty slaveComponents, controllerName - installerController, one datanode checked',
+          e: [
+            [false, false, false],
+            [true, true, true]
+          ]
+        },
+        {
+          controllerName: 'installerController',
+          slaveComponents: [],
+          services: [
+            Em.Object.create({serviceName: 'HDFS', isSelected: true})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: 'DataNode', checked: false}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: true
+            }),
+            Em.Object.create({
+              checkboxes: Em.A([
+                Em.Object.create({isInstalled: false, title: 'client'}),
+                Em.Object.create({isInstalled: false, title: 'DataNode', checked: true}),
+                Em.Object.create({isInstalled: false, title: ''})
+              ]),
+              hasMaster: false
+            })
+          ],
+          m: 'some hosts with masters, empty slaveComponents, controllerName - installerController, one datanode checked',
+          e: [
+            [false, false, false],
+            [true, true, true]
+          ]
+        }
+      ]).forEach(function (test) {
+        it(test.m, function () {
+          controller.set('content.slaveComponents', test.slaveComponents);
+          controller.set('content.controllerName', test.controllerName);
+          if (test.services) {
+            controller.set('content.services', test.services);
+          }
+          controller.set('isMasters', false);
+          controller.loadStep();
+          var r = controller.renderSlaves(test.hostsObj);
+          expect(r.map(function (i) {
+            return i.get('checkboxes').map(function (j) {
+              return j.get('checked');
+            });
+          })).to.eql(test.e);
+        });
+      });
+
+    Em.A([
+        {
+          slaveComponents: [
+            {componentName: 'c1', hosts: [
+              {hostName: 'h1', isInstalled: false},
+              {hostName: 'h2', isInstalled: false}
+            ]},
+            {componentName: 'c2', hosts: [
+              {hostName: 'h1', isInstalled: false},
+              {hostName: 'h2', isInstalled: false}
+            ]}
+          ],
+          headers: [
+            Em.Object.create({name: 'c1', label: 'C1'}),
+            Em.Object.create({name: 'c2', label: 'C2'})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              hostName: 'h1',
+              checkboxes: [
+                Em.Object.create({
+                  title: 'C1',
+                  checked: false,
+                  isInstalled: false
+                }),
+                Em.Object.create({
+                  title: 'C2',
+                  checked: false,
+                  isInstalled: false
+                })
+              ]
+            }),
+            Em.Object.create({
+              hostName: 'h2',
+              checkboxes: [
+                Em.Object.create({
+                  title: 'C1',
+                  checked: false,
+                  isInstalled: false
+                }),
+                Em.Object.create({
+                  title: 'C2',
+                  checked: false,
+                  isInstalled: false
+                })
+              ]
+            })
+          ],
+          m: 'all Checked, nothing installed before',
+          e: {
+            checked: [
+              [true, true],
+              [true, true]
+            ],
+            isInstalled: [
+              [false, false],
+              [false, false]
+            ]
+          }
+        },
+        {
+          slaveComponents: [
+            {componentName: 'c1', hosts: [
+              {hostName: 'h1', isInstalled: true},
+              {hostName: 'h2', isInstalled: true}
+            ]},
+            {componentName: 'c2', hosts: [
+              {hostName: 'h1', isInstalled: true},
+              {hostName: 'h2', isInstalled: true}
+            ]}
+          ],
+          headers: [
+            Em.Object.create({name: 'c1', label: 'C1'}),
+            Em.Object.create({name: 'c2', label: 'C2'})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              hostName: 'h1',
+              checkboxes: [
+                Em.Object.create({
+                  title: 'C1',
+                  checked: false,
+                  isInstalled: false
+                }),
+                Em.Object.create({
+                  title: 'C2',
+                  checked: false,
+                  isInstalled: false
+                })
+              ]
+            }),
+            Em.Object.create({
+              hostName: 'h2',
+              checkboxes: [
+                Em.Object.create({
+                  title: 'C1',
+                  checked: false,
+                  isInstalled: false
+                }),
+                Em.Object.create({
+                  title: 'C2',
+                  checked: false,
+                  isInstalled: false
+                })
+              ]
+            })
+          ],
+          m: 'all Checked, all installed before',
+          e: {
+            checked: [
+              [true, true],
+              [true, true]
+            ],
+            isInstalled: [
+              [true, true],
+              [true, true]
+            ]
+          }
+        },
+        {
+          slaveComponents: [
+            {componentName: 'c1', hosts: [
+              {hostName: 'h1', isInstalled: true}
+            ]},
+            {componentName: 'c2', hosts: [
+              {hostName: 'h2', isInstalled: true}
+            ]}
+          ],
+          headers: [
+            Em.Object.create({name: 'c1', label: 'C1'}),
+            Em.Object.create({name: 'c2', label: 'C2'})
+          ],
+          hostsObj: [
+            Em.Object.create({
+              hostName: 'h1',
+              checkboxes: [
+                Em.Object.create({
+                  title: 'C1',
+                  checked: false,
+                  isInstalled: false
+                }),
+                Em.Object.create({
+                  title: 'C2',
+                  checked: false,
+                  isInstalled: false
+                })
+              ]
+            }),
+            Em.Object.create({
+              hostName: 'h2',
+              checkboxes: [
+                Em.Object.create({
+                  title: 'C1',
+                  checked: false,
+                  isInstalled: false
+                }),
+                Em.Object.create({
+                  title: 'C2',
+                  checked: false,
+                  isInstalled: false
+                })
+              ]
+            })
+          ],
+          m: 'some Checked, some installed before',
+          e: {
+            checked: [
+              [true, false],
+              [false, true]
+            ],
+            isInstalled: [
+              [true, false],
+              [false, true]
+            ]
+          }
+        }
+      ]).forEach(function (test) {
+        it(test.m, function () {
+          controller.set('content.slaveComponentHosts', test.slaveComponents);
+          controller.set('headers', test.headers);
+          var r = controller.renderSlaves(test.hostsObj);
+          var checked = r.map(function (i) {
+            return i.get('checkboxes').map(function (j) {
+              return j.get('checked')
+            })
+          });
+          var isInstalled = r.map(function (i) {
+            return i.get('checkboxes').map(function (j) {
+              return j.get('isInstalled')
+            })
+          });
+          expect(checked).to.eql(test.e.checked);
+          expect(isInstalled).to.eql(test.e.isInstalled);
+        });
+      });
+  });
+
+});

+ 170 - 0
ambari-web/test/views/wizard/step6_view_test.js

@@ -0,0 +1,170 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+require('utils/helper');
+require('utils/string_utils');
+require('views/wizard/step6_view');
+var view;
+
+describe('App.WizardStep6View', function() {
+
+  beforeEach(function() {
+    view = App.WizardStep6View.create({
+      controller: App.WizardStep6Controller.create()
+    });
+  });
+
+  describe('#content', function() {
+    it('should be same to controller.hosts', function() {
+      view.set('content', []);
+      var d = [{}, {}];
+      view.set('controller.hosts', d);
+      expect(view.get('content')).to.eql(d);
+    });
+  });
+
+  describe('#filteredContent', function() {
+    it('should be same to content', function() {
+      view.set('content', []);
+      var d = [{}, {}];
+      view.set('controller.hosts', d);
+      expect(view.get('filteredContent')).to.eql(d);
+    });
+  });
+
+  describe('#didInsertElement', function() {
+    beforeEach(function() {
+      sinon.stub(view.get('controller'), 'loadStep', Em.K);
+      sinon.stub(App, 'tooltip', Em.K);
+      sinon.stub(view, 'setLabel', Em.K);
+    });
+    afterEach(function() {
+      view.get('controller').loadStep.restore();
+      App.tooltip.restore();
+      view.setLabel.restore();
+    });
+    it('should call loadStep', function() {
+      view.didInsertElement();
+      expect(view.get('controller').loadStep.calledOnce).to.equal(true);
+    });
+    it('should create tooltip', function() {
+      view.didInsertElement();
+      expect(App.tooltip.calledOnce).to.equal(true);
+    });
+    it('should call setLabel if not controller.isMasters', function() {
+      view.set('controller.isMasters', false);
+      view.didInsertElement();
+      expect(view.setLabel.calledOnce).to.equal(true);
+    });
+    it('shouldn\'t call setLabel if controller.isMasters', function() {
+      view.set('controller.isMasters', true);
+      view.didInsertElement();
+      expect(view.setLabel.called).to.equal(false);
+    });
+  });
+
+  describe('#setLabel', function() {
+    var tests = Em.A([
+      {
+        clients: [{display_name: 'c1'}],
+        m: 'One client',
+        e: 'c1'
+      },
+      {
+        clients: [{display_name: 'c1'}, {display_name: 'c2'}],
+        m: 'Two clients',
+        e: 'c1 and c2.'
+      },
+      {
+        clients: [{display_name: 'c1'}, {display_name: 'c2'}, {display_name: 'c3'}],
+        m: 'Three clients',
+        e: 'c1, c2 and c3.'
+      },
+      {
+        clients: [{display_name: 'c1'}, {display_name: 'c2'}, {display_name: 'c3'}, {display_name: 'c4'}],
+        m: 'Four clients',
+        e: 'c1, c2, c3 and c4.'
+      },
+      {
+        clients: [{display_name: 'c1'}, {display_name: 'c2'}, {display_name: 'c3'}, {display_name: 'c4'}, {display_name: 'c5'}],
+        m: 'Five clients',
+        e: 'c1, c2, c3, c4 and c5.'
+      }
+    ]);
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        view.set('controller.content', {clients: test.clients});
+        view.setLabel();
+        expect(view.get('label').endsWith(test.e)).to.equal(true);
+      });
+    });
+  });
+  
+  describe('#checkboxView', function() {
+    it('should call checkCallback', function() {
+      var v = view.get('checkboxView').create({
+        controller: App.WizardStep6Controller.create()
+      });
+      sinon.stub(v.get('controller'), 'checkCallback', Em.K);
+      v.click();
+      expect(v.get('controller').checkCallback.calledOnce).to.equal(true);
+      v.get('controller').checkCallback.restore();
+    });
+  });
+
+});
+
+describe('App.WizardStep6HostView', function() {
+
+  beforeEach(function() {
+    view = App.WizardStep6HostView.create({
+      controller: App.WizardStep6Controller.create()
+    });
+  });
+
+  describe('#didInsertElement', function() {
+    beforeEach(function() {
+      sinon.stub(App, 'popover', Em.K);
+    });
+    afterEach(function() {
+      App.popover.restore();
+    });
+    it('should create popover if not controller.isMasters', function() {
+      sinon.stub(view.get('controller'), 'getMasterComponentsForHost', function() {return [{}, {}];});
+      view.set('controller.isMasters', false);
+      view.didInsertElement();
+      expect(App.popover.calledOnce).to.equal(true);
+      view.get('controller').getMasterComponentsForHost.restore();
+    });
+    it('shouldn\'t create popover if controller.isMasters', function() {
+      view.set('controller.isMasters', true);
+      view.didInsertElement();
+      expect(App.popover.called).to.equal(false);
+    });
+    it('shouldn\'t create popover if controller.getMasterComponentsForHost returns empty array', function() {
+      sinon.stub(view.get('controller'), 'getMasterComponentsForHost', function() {return [];});
+      view.set('controller.isMasters', true);
+      view.didInsertElement();
+      expect(App.popover.called).to.equal(false);
+      view.get('controller').getMasterComponentsForHost.restore();
+    });
+  });
+
+});