You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2016/04/13 17:51:42 UTC

ambari git commit: AMBARI-15859 Cover wizard specific steps with unit tests. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 4938e8479 -> 9741e4aab


AMBARI-15859 Cover wizard specific steps with unit tests. (atkach)


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

Branch: refs/heads/trunk
Commit: 9741e4aab6f8c9f11798ce4f872867e3313bf328
Parents: 4938e84
Author: Andrii Tkach <at...@apache.org>
Authored: Wed Apr 13 16:27:26 2016 +0300
Committer: Andrii Tkach <at...@apache.org>
Committed: Wed Apr 13 18:51:17 2016 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   2 +
 .../progress_popup_controller.js                |  26 +-
 .../main/host/addHost/step4_controller.js       |   8 +-
 .../wizard/step7/assign_master_controller.js    |  48 ++-
 ambari-web/app/messages.js                      |   2 +
 .../views/wizard/step7/assign_master_view.js    |  57 ++-
 .../progress_popup_controller_test.js           | 307 +++++++++++++++
 .../main/host/addHost/step4_controller_test.js  |  32 +-
 .../step7/assign_master_controller_test.js      | 387 +++++++++++++++++++
 .../wizard/step7/assign_master_view_test.js     | 130 +++++++
 10 files changed, 941 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/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 db504f2..33e7f1c 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -120,6 +120,7 @@ var files = [
   'test/controllers/wizard/step6_test',
   'test/controllers/wizard/step7_test',
   'test/controllers/wizard/step7/pre_install_checks_controller_test',
+  'test/controllers/wizard/step7/assign_master_controller_test',
   'test/controllers/wizard/step8_test',
   'test/controllers/wizard/step9_test',
   'test/controllers/wizard/step10_test',
@@ -338,6 +339,7 @@ var files = [
   'test/views/wizard/step8_view_test',
   'test/views/wizard/step9_view_test',
   'test/views/wizard/step9/hostLogPopupBody_view_test',
+  'test/views/wizard/step7/assign_master_view_test',
   'test/views/wizard/step10_view_test',
   'test/views/application_test',
   'test/views/installer_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/app/controllers/main/admin/highAvailability/progress_popup_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/highAvailability/progress_popup_controller.js b/ambari-web/app/controllers/main/admin/highAvailability/progress_popup_controller.js
index 0c15dd6..06dd1cd 100644
--- a/ambari-web/app/controllers/main/admin/highAvailability/progress_popup_controller.js
+++ b/ambari-web/app/controllers/main/admin/highAvailability/progress_popup_controller.js
@@ -81,7 +81,7 @@ App.HighAvailabilityProgressPopupController = Ember.Controller.extend({
    * @param stageId {Number}
    */
   initPopup: function (popupTitle, requestIds, progressController, showSpinner, stageId) {
-    if(showSpinner){
+    if (showSpinner) {
       var loadingPopup = App.ModalPopup.show({
         header: Em.I18n.t('jobs.loadingTasks'),
         primary: false,
@@ -92,11 +92,13 @@ App.HighAvailabilityProgressPopupController = Ember.Controller.extend({
       });
       this.set('spinnerPopup', loadingPopup);
     }
-    this.set('progressController', progressController);
-    this.set('popupTitle', popupTitle);
-    this.set('requestIds', requestIds);
-    this.set('hostsData', []);
-    this.set('stageId', stageId);
+    this.setProperties({
+      progressController: progressController,
+      popupTitle: popupTitle,
+      requestIds: requestIds,
+      hostsData: [],
+      stageId: stageId
+    });
     this.getHosts();
   },
 
@@ -141,7 +143,7 @@ App.HighAvailabilityProgressPopupController = Ember.Controller.extend({
         this.addObserver('progressController.logs.length', this, 'getDataFromProgressController');
       }
     }
-    if(this.get('spinnerPopup')){
+    if (this.get('spinnerPopup')) {
       this.get('spinnerPopup').hide();
       this.set('spinnerPopup', null);
     }
@@ -155,6 +157,7 @@ App.HighAvailabilityProgressPopupController = Ember.Controller.extend({
     var hosts = [];
     var hostsMap = {};
     var popupTitle = this.get('popupTitle');
+
     data.forEach(function (request) {
       request.tasks.forEach(function (task) {
         var host = task.Tasks.host_name;
@@ -190,7 +193,7 @@ App.HighAvailabilityProgressPopupController = Ember.Controller.extend({
     var stageId = this.get('stageId');
     // If the progress page is broken into stages then update host with only stage's tasks
     if (!!stageId) {
-      tasksData = this.get('progressController.logs').filterProperty('Tasks.stage_id',stageId);
+      tasksData = this.get('progressController.logs').filterProperty('Tasks.stage_id', stageId);
     } else {
       tasksData = this.get('progressController.logs');
     }
@@ -216,8 +219,11 @@ App.HighAvailabilityProgressPopupController = Ember.Controller.extend({
     var result = false;
     requests.forEach(function (request) {
       if ((request.Requests.task_count -
-          (request.Requests.aborted_task_count + request.Requests.completed_task_count + request.Requests.failed_task_count
-              + request.Requests.timed_out_task_count - request.Requests.queued_task_count)) > 0) {
+          (request.Requests.aborted_task_count +
+           request.Requests.completed_task_count +
+           request.Requests.failed_task_count +
+           request.Requests.timed_out_task_count -
+           request.Requests.queued_task_count)) > 0) {
         result = true;
       }
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/app/controllers/main/host/addHost/step4_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/addHost/step4_controller.js b/ambari-web/app/controllers/main/host/addHost/step4_controller.js
index 7025a3a..7049842 100644
--- a/ambari-web/app/controllers/main/host/addHost/step4_controller.js
+++ b/ambari-web/app/controllers/main/host/addHost/step4_controller.js
@@ -18,9 +18,9 @@
 
 var App = require('app');
 
-App.AddHostStep4Controller= Em.Controller.extend({
+App.AddHostStep4Controller = Em.Controller.extend({
 
-  name:"addHostStep4Controller",
+  name: "addHostStep4Controller",
   isConfigGroupLoaded: false,
 
   loadConfigGroups: function () {
@@ -33,11 +33,11 @@ App.AddHostStep4Controller= Em.Controller.extend({
   },
 
   successLoadingConfigGroup: function (data) {
-    App.router.get('addHostController.content').set('configGroups',data.items);
+    App.router.get('addHostController.content').set('configGroups', data.items);
     this.set('isConfigGroupLoaded', true);
   },
 
-  errorLoadingConfigGroup: function(data) {
+  errorLoadingConfigGroup: function (data) {
     App.router.get('addHostController.content').set('configGroups', []);
     this.set('isConfigGroupLoaded', true);
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step7/assign_master_controller.js b/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
index 57230c4..196932d 100644
--- a/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
+++ b/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
@@ -17,10 +17,11 @@
  */
 
 var stringUtils = require('utils/string_utils');
+var numberUtils = require('utils/number_utils');
 
 App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App.AssignMasterComponents, {
 
-  name:"assignMasterOnStep7Controller",
+  name: "assignMasterOnStep7Controller",
 
   useServerValidation: false,
 
@@ -30,7 +31,7 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
 
   configActionComponent: {},
 
-  content: function() {
+  content: function () {
     return (this.get('configWidgetContext.controller.content') || {});
   }.property('configWidgetContext.controller.content'),
 
@@ -48,14 +49,14 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
    * @public
    * @method {execute}
    */
-  execute: function(context, action, hostComponent) {
+  execute: function (context, action, hostComponent) {
     this.set('configWidgetContext', context);
     this.set('content', context.get('controller.content'));
-    this.set('configActionComponent',hostComponent);
+    this.set('configActionComponent', hostComponent);
     this.set('mastersToCreate', [hostComponent.componentName]);
     var missingDependentServices = this.getAllMissingDependentServices();
     var isNonWizardPage = !this.get('content.controllerName');
-    switch(action) {
+    switch (action) {
       case 'ADD':
         if (missingDependentServices.length && isNonWizardPage) {
           this.showInstallServicesPopup(missingDependentServices);
@@ -74,7 +75,7 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
    * @private
    * @method
    */
-  showAssignComponentPopup: function() {
+  showAssignComponentPopup: function () {
     var self = this;
     // Master component hosts should be loaded only when content.controller name is not defined i.e non-wizard pages
     if (!this.get('content.controllerName')) {
@@ -103,13 +104,12 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
    * Displays the popup to install required service dependencies for being added component with this config change
    * @param missingDependentServices {String[]}   Array of service display names
    */
-  showInstallServicesPopup: function(missingDependentServices) {
-    var self = this;
-    var displayServices =  stringUtils.getFormattedStringFromArray(missingDependentServices);
+  showInstallServicesPopup: function (missingDependentServices) {
+    var displayServices = stringUtils.getFormattedStringFromArray(missingDependentServices);
     var configWidgetContext = this.get('configWidgetContext');
-    var config =  self.get('configWidgetContext.config');
-    var configDisplayName =  config.get('displayName').toLowerCase();
-    App.ModalPopup.show({
+    var config = this.get('configWidgetContext.config');
+    var configDisplayName = config.get('displayName').toLowerCase();
+    return App.ModalPopup.show({
       header: Em.I18n.t('installer.step7.missing.service.header'),
       body: Em.I18n.t('installer.step7.missing.service.body').format(displayServices, configDisplayName),
       primaryClass: 'btn-danger',
@@ -134,7 +134,7 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
    * @private
    * @method {removeMasterComponent}
    */
-  removeMasterComponent: function() {
+  removeMasterComponent: function () {
     var componentsToDelete = this.get('mastersToCreate');
     if (this.get('content.controllerName')) {
       var parentController = App.router.get(this.get('content.controllerName'));
@@ -155,7 +155,6 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
    * @method renderHostInfo
    */
   renderHostInfo: function () {
-    var numberUtils = require('utils/number_utils');
     var parentController = this.get('content.controllerName');
     if (parentController) {
       this._super();
@@ -173,7 +172,7 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
       }
 
       this.set("hosts", result);
-      this.sortHosts(this.get('hosts'));
+      this.sortHosts(result);
       this.set('isLoaded', true);
     }
   },
@@ -187,7 +186,7 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
   loadMasterComponentHosts: function () {
     var stackMasterComponents = App.get('components.masters').uniq();
     var masterComponentHosts = [];
-    App.HostComponent.find().filter(function(component) {
+    App.HostComponent.find().filter(function (component) {
       return stackMasterComponents.contains(component.get('componentName'));
     }).forEach(function (item) {
       masterComponentHosts.push({
@@ -207,17 +206,16 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
    * @method getAllMissingDependentServices
    * @return  missingDependentServices {Array}
    */
-  getAllMissingDependentServices: function() {
-    var context = this.get('configWidgetContext');
+  getAllMissingDependentServices: function () {
     var configActionComponentName = this.get('configActionComponent').componentName;
     var componentStackService = App.StackServiceComponent.find(configActionComponentName).get('stackService');
     var dependentServices = componentStackService.get('requiredServices');
-    var missingDependentServices =  dependentServices.filter(function(item) {
+
+    return dependentServices.filter(function (item) {
       return !App.Service.find().findProperty('serviceName', item);
-    }).map(function(item){
-       return  App.StackService.find(item).get('displayName');
+    }).map(function (item) {
+      return App.StackService.find(item).get('displayName');
     });
-    return missingDependentServices;
   },
 
 
@@ -234,14 +232,14 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
     var selectedServicesMasters = this.get('selectedServicesMasters');
     var context = this.get('configWidgetContext');
     var configActionComponent = this.get('configActionComponent');
-    var componentHostName = selectedServicesMasters.findProperty('component_name',configActionComponent.componentName).selectedHost;
+    var componentHostName = selectedServicesMasters.findProperty('component_name', configActionComponent.componentName).selectedHost;
 
     var hostComponentConfig = context.get('config.configAction.hostComponentConfig');
     var serviceConfigs = context.get('controller.stepConfigs').findProperty('serviceName', context.get('config.serviceName')).get('configs');
-    var config =  serviceConfigs.filterProperty('filename', hostComponentConfig.fileName).findProperty('name', hostComponentConfig.configName);
+    var config = serviceConfigs.filterProperty('filename', hostComponentConfig.fileName).findProperty('name', hostComponentConfig.configName);
     config.set('value', componentHostName);
     config.set('recommendedValue', componentHostName);
-    configActionComponent.hostName =  componentHostName;
+    configActionComponent.hostName = componentHostName;
     this.get('configWidgetContext.config').set('configActionComponent', configActionComponent);
   }
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 5f95cb8..3ede13a 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -79,6 +79,8 @@ Em.I18n.translations = {
   'op': 'op',
   'ops': 'ops',
   'or': 'or',
+  'then': 'then',
+  'it': 'it',
 
 
   'common.access':'Access',

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/app/views/wizard/step7/assign_master_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step7/assign_master_view.js b/ambari-web/app/views/wizard/step7/assign_master_view.js
index c8b9939..1ad818a 100644
--- a/ambari-web/app/views/wizard/step7/assign_master_view.js
+++ b/ambari-web/app/views/wizard/step7/assign_master_view.js
@@ -34,29 +34,50 @@ App.AssignMasterOnStep7View = App.AssignMasterComponentsView.extend({
 
   willInsertElement: function() {
     this._super();
-    var mastersToCreate = this.get('controller.mastersToCreate');
-    var mastersToCreateDisplayName =  mastersToCreate.map(function(item){
-      return App.StackServiceComponent.find().findProperty('componentName', item).get('displayName');
-    });
-    var stringText1 =  mastersToCreate.length > 1 ? 'hosts' : 'host';
-    var stringText2 =  mastersToCreate.length > 1 ? 'them' : 'it';
-    var alertMessage = Em.I18n.t('installer.step7.assign.master.body').format(mastersToCreateDisplayName.join(), stringText1, stringText2);
-    var dependentComponents = [];
-    mastersToCreate.forEach(function(_component){
-      var dependencies = App.StackServiceComponent.find(_component).get('dependencies').filterProperty('scope', 'host').map(function(item){
-        return App.StackServiceComponent.find().findProperty('componentName', item.componentName).get('displayName');
-      });
-      dependentComponents = dependentComponents.concat(dependencies).uniq();
-    }, this);
+    this.setAlertMessage();
+  },
+
+  /**
+   * @method setAlertMessage
+   */
+  setAlertMessage: function() {
+    var mastersToCreate = this.get('controller.mastersToCreate'),
+        mastersToCreateDisplayName = mastersToCreate.map(function (item) {
+          return App.format.role(item);
+        }),
+        stringText1 = mastersToCreate.length > 1 ? Em.I18n.t('common.hosts').toLowerCase() : Em.I18n.t('common.host').toLowerCase(),
+        stringText2 = mastersToCreate.length > 1 ? Em.I18n.t('then') : Em.I18n.t('it'),
+        alertMessage = Em.I18n.t('installer.step7.assign.master.body').format(mastersToCreateDisplayName.join(), stringText1, stringText2),
+        dependentComponents = this.getDependentComponents(mastersToCreate),
+        isManualKerberos = App.get('router.mainAdminKerberosController.isManualKerberos');
+
     if (dependentComponents.length) {
-      alertMessage += '<br/>' + Em.I18n.t('installer.step7.assign.master.dependent.component.body').format(stringUtils.getFormattedStringFromArray(dependentComponents));
+      alertMessage += '<br/>' + Em.I18n.t('installer.step7.assign.master.dependent.component.body')
+                                .format(stringUtils.getFormattedStringFromArray(dependentComponents));
     }
-    var isManualKerberos = App.get('router.mainAdminKerberosController.isManualKerberos');
+
     if (isManualKerberos) {
-      var warnMessage =  Em.I18n.t('common.important') + ': ' + Em.I18n.t('installer.step8.kerberors.warning');
+      var warnMessage = Em.I18n.t('common.important') + ': ' + Em.I18n.t('installer.step8.kerberors.warning');
       alertMessage += '<br/>' + Em.I18n.t('common.warn.message').format(warnMessage);
     }
-    this.set('alertMessage', alertMessage)
+    this.set('alertMessage', alertMessage);
+  },
+
+  /**
+   * @method getDependentComponents
+   * @param {Array} mastersToCreate
+   * @returns {Array}
+   */
+  getDependentComponents: function(mastersToCreate) {
+    var dependentComponents = [];
+
+    mastersToCreate.forEach(function(_component) {
+      var dependencies = App.StackServiceComponent.find(_component).get('dependencies').filterProperty('scope', 'host').map(function (item) {
+        return App.format.role(item.componentName);
+      });
+      dependentComponents = dependentComponents.concat(dependencies).uniq();
+    }, this);
+    return dependentComponents;
   }
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/test/controllers/main/admin/highAvailability/progress_popup_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/admin/highAvailability/progress_popup_controller_test.js b/ambari-web/test/controllers/main/admin/highAvailability/progress_popup_controller_test.js
index f4a0e99..c7ee374 100644
--- a/ambari-web/test/controllers/main/admin/highAvailability/progress_popup_controller_test.js
+++ b/ambari-web/test/controllers/main/admin/highAvailability/progress_popup_controller_test.js
@@ -223,4 +223,311 @@ describe('App.HighAvailabilityProgressPopupController', function () {
 
   });
 
+  describe("#initPopup()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App.ModalPopup, 'show');
+      sinon.stub(controller, 'getHosts');
+      sinon.stub(controller, 'setProperties');
+    });
+
+    afterEach(function() {
+      App.ModalPopup.show.restore();
+      controller.getHosts.restore();
+      controller.setProperties.restore();
+    });
+
+    it("App.ModalPopup.show should be called", function() {
+      controller.initPopup(null, null, null, true, null);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+
+    it("setProperties should be called", function() {
+      controller.initPopup('popupTitle', [], {}, false, 1);
+      expect(controller.setProperties.calledWith({
+        progressController: {},
+        popupTitle: 'popupTitle',
+        requestIds: [],
+        hostsData: [],
+        stageId: 1
+      })).to.be.true;
+    });
+
+    it("getHosts should be called", function() {
+      controller.initPopup(null, null, null, false, null);
+      expect(controller.getHosts.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#onGetHostsSuccess()", function () {
+    var spinner = Em.Object.create({
+      hide: Em.K
+    });
+
+    beforeEach(function() {
+      sinon.stub(controller, 'calculateHostsData');
+      sinon.stub(App.HostPopup, 'initPopup');
+      sinon.stub(controller, 'isRequestRunning').returns(true);
+      sinon.stub(controller, 'addObserver');
+      sinon.stub(spinner, 'hide');
+      controller.setProperties({
+        requestIds: [1],
+        hostsData: [],
+        popupTitle: 'popupTitle',
+        spinnerPopup: spinner
+      });
+      controller.onGetHostsSuccess({});
+    });
+
+    afterEach(function() {
+      controller.calculateHostsData.restore();
+      App.HostPopup.initPopup.restore();
+      controller.isRequestRunning.restore();
+      controller.addObserver.restore();
+      spinner.hide.restore();
+    });
+
+    it("calculateHostsData should be called", function() {
+      expect(controller.calculateHostsData.calledWith([{}])).to.be.true;
+    });
+
+    it("App.HostPopup.initPopup should be called", function() {
+      expect(App.HostPopup.initPopup.calledWith('popupTitle', controller)).to.be.true;
+    });
+
+    it("addObserver should be called", function() {
+      expect(controller.addObserver.calledWith('progressController.logs.length', controller, 'getDataFromProgressController')).to.be.true;
+    });
+
+    it("spinnerPopup.hide should be called", function() {
+      expect(spinner.hide.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#calculateHostsData()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App, 'dateTime').returns('dateTime');
+      sinon.stub(controller, 'isRequestRunning').returns(false);
+      sinon.stub(controller, 'removeObserver');
+    });
+
+    afterEach(function() {
+      App.dateTime.restore();
+      controller.isRequestRunning.restore();
+      controller.removeObserver.restore();
+    });
+
+    it("calculate data", function() {
+      var data = [
+        {
+          tasks: [
+            {
+              Tasks: {
+                name: 't1',
+                host_name: 'host1'
+              }
+            },
+            {
+              Tasks: {
+                name: 't2',
+                host_name: 'host1'
+              }
+            }
+          ]
+        }
+      ];
+      controller.setProperties({
+        popupTitle: 'popupTitle'
+      });
+      controller.calculateHostsData(data);
+      expect(controller.get('services')).to.be.eql([
+        {
+          "name": "popupTitle",
+          "hosts": [
+            {
+              "name": "host1",
+              "publicName": "host1",
+              "logTasks": [
+                {
+                  "Tasks": {
+                    "name": "t1",
+                    "host_name": "host1"
+                  }
+                },
+                {
+                  "Tasks": {
+                    "name": "t2",
+                    "host_name": "host1"
+                  }
+                }
+              ]
+            }
+          ]
+        }
+      ]);
+      expect(controller.get('serviceTimestamp')).to.be.equal('dateTime');
+      expect(controller.removeObserver.calledWith('progressController.logs.length', controller, 'getDataFromProgressController')).to.be.true;
+    });
+  });
+
+  describe("#getDataFromProgressController()", function () {
+
+    beforeEach(function() {
+      sinon.stub(controller, 'calculateHostsData');
+    });
+
+    afterEach(function() {
+      controller.calculateHostsData.restore();
+    });
+
+    it("empty logs", function() {
+      controller.set('progressController', Em.Object.create({
+        logs: []
+      }));
+      controller.getDataFromProgressController();
+      expect(controller.calculateHostsData.calledOnce).to.be.false;
+    });
+
+    it("filtered logs", function() {
+      controller.setProperties({
+        progressController: Em.Object.create({
+          logs: [
+            {
+              Tasks: {
+                stage_id: 1,
+                request_id: 1
+              }
+            }
+          ]
+        }),
+        stageId: 1,
+        hostsData: [
+          {
+            Requests: {
+              id: 1
+            }
+          }
+        ]
+      });
+      controller.getDataFromProgressController();
+      expect(controller.calculateHostsData.calledWith([
+        {
+          Requests: {
+            id: 1
+          },
+          tasks: [
+            {
+              Tasks: {
+                stage_id: 1,
+                request_id: 1
+              }
+            }
+          ]
+        }
+      ])).to.be.true;
+    });
+  });
+
+  describe("#isRequestRunning()", function () {
+    var testCases = [
+      {
+        data: [
+          {
+            Requests: {
+              task_count: 1,
+              aborted_task_count: 1,
+              completed_task_count: 0,
+              failed_task_count: 0,
+              timed_out_task_count: 0,
+              queued_task_count: 0
+            }
+          }
+        ],
+        expected: false
+      },
+      {
+        data: [
+          {
+            Requests: {
+              task_count: 1,
+              aborted_task_count: 0,
+              completed_task_count: 1,
+              failed_task_count: 0,
+              timed_out_task_count: 0,
+              queued_task_count: 0
+            }
+          }
+        ],
+        expected: false
+      },
+      {
+        data: [
+          {
+            Requests: {
+              task_count: 1,
+              aborted_task_count: 0,
+              completed_task_count: 0,
+              failed_task_count: 1,
+              timed_out_task_count: 0,
+              queued_task_count: 0
+            }
+          }
+        ],
+        expected: false
+      },
+      {
+        data: [
+          {
+            Requests: {
+              task_count: 1,
+              aborted_task_count: 0,
+              completed_task_count: 0,
+              failed_task_count: 0,
+              timed_out_task_count: 1,
+              queued_task_count: 0
+            }
+          }
+        ],
+        expected: false
+      },
+      {
+        data: [
+          {
+            Requests: {
+              task_count: 1,
+              aborted_task_count: 0,
+              completed_task_count: 0,
+              failed_task_count: 0,
+              timed_out_task_count: 0,
+              queued_task_count: 1
+            }
+          }
+        ],
+        expected: true
+      },
+      {
+        data: [
+          {
+            Requests: {
+              task_count: 1,
+              aborted_task_count: 0,
+              completed_task_count: 0,
+              failed_task_count: 0,
+              timed_out_task_count: 0,
+              queued_task_count: 0
+            }
+          }
+        ],
+        expected: true
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it("request: " + JSON.stringify(test.data), function() {
+        expect(controller.isRequestRunning(test.data)).to.be.equal(test.expected);
+      });
+    });
+  });
+
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/test/controllers/main/host/addHost/step4_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host/addHost/step4_controller_test.js b/ambari-web/test/controllers/main/host/addHost/step4_controller_test.js
index 7d9ffc7..f08d9b6 100644
--- a/ambari-web/test/controllers/main/host/addHost/step4_controller_test.js
+++ b/ambari-web/test/controllers/main/host/addHost/step4_controller_test.js
@@ -17,13 +17,43 @@
  */
 
 var App = require('app');
+var testHelpers = require('test/helpers');
 
 describe('App.AddHostStep4Controller', function() {
   var controller;
+
+  before(function() {
+    controller = App.AddHostStep4Controller.create();
+  });
+
+  describe("#loadConfigGroups()", function () {
+
+    it("App.ajax.send should be called", function() {
+      controller.loadConfigGroups();
+      var args = testHelpers.filterAjaxRequests('name', 'config_groups.all_fields');
+      expect(args[0][0]).to.eql({
+        name: 'config_groups.all_fields',
+        sender: controller,
+        success: 'successLoadingConfigGroup',
+        error: 'errorLoadingConfigGroup'
+      });
+    });
+  });
+
+  describe('#successLoadingConfigGroup()', function() {
+    before(function() {
+      controller.successLoadingConfigGroup({items: [{}]});
+    });
+    it('should set config groups on succeeded request', function() {
+      expect(App.router.get('addHostController.content.configGroups')).to.eql([{}]);
+    });
+    it('should set `isConfigGroupLoaded` to true', function() {
+      expect(controller.get('isConfigGroupLoaded')).to.true;
+    });
+  });
   
   describe('#errorLoadingConfigGroup()', function() {
     before(function() {
-      controller = App.AddHostStep4Controller.create({});
       controller.errorLoadingConfigGroup();
     });
     it('should set config groups on failed request', function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js b/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js
new file mode 100644
index 0000000..2d467c2
--- /dev/null
+++ b/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js
@@ -0,0 +1,387 @@
+/**
+ * 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 stringUtils = require('utils/string_utils');
+var numberUtils = require('utils/number_utils');
+require('models/stack_service_component');
+
+describe('App.AssignMasterOnStep7Controller', function () {
+  var view;
+
+  beforeEach(function () {
+    view = App.AssignMasterOnStep7Controller.create();
+  });
+
+  describe("#content", function () {
+
+    it("content is correct", function () {
+      view.set('configWidgetContext.controller', Em.Object.create({
+        content: {'name': 'name'}
+      }));
+      view.propertyDidChange('content');
+      expect(view.get('content')).to.be.eql({'name': 'name'});
+    });
+
+    it("content is null", function () {
+      view.set('configWidgetContext.controller', Em.Object.create({
+        content: null
+      }));
+      view.propertyDidChange('content');
+      expect(view.get('content')).to.be.empty;
+    });
+  });
+
+  describe("#execute()", function () {
+    var context = Em.Object.create({
+      controller: {
+        content: Em.Object.create({
+          controllerName: ""
+        })
+      }
+    });
+
+    beforeEach(function() {
+      this.mock = sinon.stub(view, 'getAllMissingDependentServices');
+      sinon.stub(view, 'showInstallServicesPopup');
+      sinon.stub(view, 'showAssignComponentPopup');
+      sinon.stub(view, 'removeMasterComponent');
+      view.reopen({
+        content: Em.Object.create()
+      });
+    });
+
+    afterEach(function() {
+      this.mock.restore();
+      view.showInstallServicesPopup.restore();
+      view.showAssignComponentPopup.restore();
+      view.removeMasterComponent.restore();
+    });
+
+    it("ADD action, controllerName is empty", function() {
+      this.mock.returns([{}]);
+      view.execute(context, 'ADD', {componentName: 'C1'});
+      expect(view.showInstallServicesPopup.calledOnce).to.be.true;
+    });
+
+    it("ADD action, controllerName is set", function() {
+      context = Em.Object.create({
+        controller: {
+          content: Em.Object.create({
+            controllerName: "ctrl1"
+          })
+        }
+      });
+      this.mock.returns([{}]);
+      view.execute(context, 'ADD', {componentName: 'C1'});
+      expect(view.showAssignComponentPopup.calledOnce).to.be.true;
+    });
+
+    it("ADD action, no dependent services", function() {
+      this.mock.returns([]);
+      view.execute(context, 'ADD', {componentName: 'C1'});
+      expect(view.showAssignComponentPopup.calledOnce).to.be.true;
+    });
+
+    it("DELETE action", function() {
+      this.mock.returns([{}]);
+      view.execute(context, 'DELETE', {componentName: 'C1'});
+      expect(view.removeMasterComponent.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#showAssignComponentPopup()", function () {
+
+    beforeEach(function() {
+      sinon.stub(view, 'loadMasterComponentHosts');
+      sinon.stub(App.ModalPopup, 'show');
+    });
+
+    afterEach(function() {
+      view.loadMasterComponentHosts.restore();
+      App.ModalPopup.show.restore();
+    });
+
+    it("loadMasterComponentHosts should be called", function() {
+      view.reopen({
+        content: {
+          controllerName: null
+        }
+      });
+      view.showAssignComponentPopup();
+      expect(view.loadMasterComponentHosts.calledOnce).to.be.true;
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+
+    it("loadMasterComponentHosts should not be called", function() {
+      view.reopen({
+        content: {
+          controllerName: 'ctrl1'
+        }
+      });
+      view.showAssignComponentPopup();
+      expect(view.loadMasterComponentHosts.called).to.be.false;
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#showInstallServicesPopup()", function () {
+    var mock = Em.Object.create({
+      config: Em.Object.create({
+        initialValue: 'init',
+        value: '',
+        displayName: 'c1'
+      }),
+      setValue: Em.K
+    });
+
+    beforeEach(function() {
+      sinon.stub(stringUtils, 'getFormattedStringFromArray');
+      sinon.stub(mock, 'setValue');
+      sinon.spy(App.ModalPopup, 'show');
+    });
+
+    afterEach(function() {
+      stringUtils.getFormattedStringFromArray.restore();
+      mock.setValue.restore();
+      App.ModalPopup.show.restore();
+    });
+
+    it("test", function() {
+      view.set('configWidgetContext', mock);
+      var popup = view.showInstallServicesPopup();
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(mock.get('config.value')).to.be.equal('init');
+      expect(mock.setValue.calledWith('init')).to.be.true;
+    });
+  });
+
+  describe("#removeMasterComponent()", function () {
+    var mock = {
+      setDBProperty: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.stub(App.router, 'get').returns(mock);
+      sinon.stub(mock, 'setDBProperty');
+    });
+
+    afterEach(function() {
+      App.router.get.restore();
+      mock.setDBProperty.restore();
+    });
+
+    it("should set masterComponentHosts", function() {
+      view.reopen({
+        content: Em.Object.create({
+          controllerName: 'ctrl1',
+          masterComponentHosts: [
+            {component: 'C1'},
+            {component: 'C2'}
+          ]
+        }),
+        configWidgetContext: {
+          config: Em.Object.create()
+        }
+      });
+      view.set('mastersToCreate', ['C2']);
+      view.removeMasterComponent();
+      expect(view.get('content.masterComponentHosts')).to.be.eql([{component: 'C1'}]);
+      expect(mock.setDBProperty.calledWith('masterComponentHosts', [{component: 'C1'}])).to.be.true;
+    });
+  });
+
+  describe("#renderHostInfo()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App.Host, 'find').returns([
+        Em.Object.create({
+          hostName: 'host1',
+          cpu: 1,
+          memory: 1,
+          diskInfo: {}
+        })
+      ]);
+      sinon.stub(view, 'sortHosts');
+      sinon.stub(numberUtils, 'bytesToSize').returns(1);
+    });
+
+    afterEach(function() {
+      App.Host.find.restore();
+      view.sortHosts.restore();
+      numberUtils.bytesToSize.restore();
+    });
+
+    it("should set hosts", function() {
+      view.reopen({
+        content: Em.Object.create({
+          controllerName: null
+        })
+      });
+      view.renderHostInfo();
+      expect(view.get('hosts')).to.be.eql([Em.Object.create({
+        host_name: 'host1',
+        cpu: 1,
+        memory: 1,
+        disk_info: {},
+        host_info: Em.I18n.t('installer.step5.hostInfo').fmt('host1', 1, 1)
+      })]);
+      expect(view.sortHosts.calledWith([Em.Object.create({
+        host_name: 'host1',
+        cpu: 1,
+        memory: 1,
+        disk_info: {},
+        host_info: Em.I18n.t('installer.step5.hostInfo').fmt('host1', 1, 1)
+      })])).to.be.true;
+    });
+  });
+
+  describe("#loadMasterComponentHosts()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App.HostComponent, 'find').returns([
+        Em.Object.create({
+          componentName: 'C1'
+        }),
+        Em.Object.create({
+          componentName: 'C2'
+        })
+      ]);
+      sinon.stub(App, 'get').returns(['C2']);
+    });
+
+    afterEach(function() {
+      App.get.restore();
+      App.HostComponent.find.restore();
+    });
+
+    it("should set master components", function() {
+      view.loadMasterComponentHosts();
+      expect(view.get('masterComponentHosts').mapProperty('component')).to.be.eql(['C2']);
+    });
+  });
+
+  describe("#getAllMissingDependentServices()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App.StackServiceComponent, 'find').returns(Em.Object.create({
+        stackService: Em.Object.create({
+          requiredServices: ['S1', 'S2']
+        })
+      }));
+      sinon.stub(App.Service, 'find').returns([
+        {serviceName: 'S1'}
+      ]);
+      sinon.stub(App.StackService, 'find', function(input) {
+        return Em.Object.create({displayName: input});
+      });
+    });
+
+    afterEach(function() {
+      App.StackServiceComponent.find.restore();
+      App.Service.find.restore();
+      App.StackService.find.restore();
+    });
+
+    it("test", function() {
+      view.set('configActionComponent', Em.Object.create({
+        componentName: 'C1'
+      }));
+      expect(view.getAllMissingDependentServices()).to.be.eql(['S2']);
+    });
+  });
+
+  describe("#submit()", function () {
+    var popup = {
+      hide: Em.K
+      },
+      router = {
+        saveMasterComponentHosts: Em.K
+      },
+      config = Em.Object.create({
+        filename: 'file1',
+        name: 'conf1'
+      });
+
+    beforeEach(function() {
+      sinon.stub(popup, 'hide');
+      sinon.stub(App.router, 'get').returns(router);
+      sinon.stub(router, 'saveMasterComponentHosts');
+      view.reopen({
+        content: Em.Object.create({
+          controllerName: 'ctrl1'
+        }),
+        selectedServicesMasters: [
+          {
+            component_name: 'C1',
+            selectedHost: 'host1'
+          }
+        ],
+        popup: popup,
+        configActionComponent: {
+          componentName: 'C1'
+        },
+        configWidgetContext: Em.Object.create({
+          config: Em.Object.create({
+            configAction: {
+              hostComponentConfig: {
+                fileName: 'file1',
+                configName: 'conf1'
+              }
+            },
+            serviceName: 'S1'
+          }),
+          controller: Em.Object.create({
+            stepConfigs: [
+              Em.Object.create({
+                serviceName: 'S1',
+                configs: [
+                  config
+                ]
+              })
+            ]
+          })
+        })
+      });
+      view.submit();
+    });
+
+    afterEach(function() {
+      App.router.get.restore();
+      popup.hide.restore();
+      router.saveMasterComponentHosts.restore();
+    });
+
+    it("saveMasterComponentHosts should be called", function() {
+      expect(router.saveMasterComponentHosts.calledOnce).to.be.true;
+    });
+
+    it("configActionComponent should be set", function() {
+      expect(view.get('configWidgetContext.config.configActionComponent')).to.be.eql({
+        componentName: 'C1',
+        hostName: 'host1'
+      });
+    });
+
+    it("config should be set", function() {
+      expect(config.get('value')).to.be.equal('host1');
+      expect(config.get('recommendedValue')).to.be.equal('host1');
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9741e4aa/ambari-web/test/views/wizard/step7/assign_master_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/wizard/step7/assign_master_view_test.js b/ambari-web/test/views/wizard/step7/assign_master_view_test.js
new file mode 100644
index 0000000..1aaec7e
--- /dev/null
+++ b/ambari-web/test/views/wizard/step7/assign_master_view_test.js
@@ -0,0 +1,130 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/wizard/step9/hostLogPopupBody_view');
+var stringUtils = require('utils/string_utils');
+var view;
+
+function getView() {
+  return App.AssignMasterOnStep7View.create({
+    controller: Em.Object.create()
+  });
+}
+
+describe('App.AssignMasterOnStep7View', function() {
+
+  beforeEach(function() {
+    view = getView();
+  });
+
+  describe("#willInsertElement()", function () {
+
+    beforeEach(function() {
+      sinon.stub(view, 'setAlertMessage');
+    });
+
+    afterEach(function() {
+      view.setAlertMessage.restore();
+    });
+
+    it("setAlertMessage should be called", function() {
+      view.willInsertElement();
+      expect(view.setAlertMessage.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#getDependentComponents()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App.StackServiceComponent, 'find').returns(Em.Object.create({
+        dependencies: [{
+          scope: 'host',
+          componentName: 'C1'
+        }]
+      }));
+      sinon.stub(App.format, 'role', function(arg) {
+        return arg;
+      });
+    });
+
+    afterEach(function() {
+      App.StackServiceComponent.find.restore();
+      App.format.role.restore();
+    });
+
+    it("should return dependent components", function() {
+      expect(view.getDependentComponents([{}])).to.be.eql(['C1']);
+    });
+  });
+
+  describe("#setAlertMessage()", function () {
+
+    beforeEach(function() {
+      sinon.stub(App.format, 'role', function(arg) {
+        return arg;
+      });
+      sinon.stub(view, 'getDependentComponents').returns(['c1']);
+      sinon.stub(stringUtils, 'getFormattedStringFromArray').returns('');
+      this.mock = sinon.stub(App, 'get');
+    });
+
+    afterEach(function() {
+      App.format.role.restore();
+      view.getDependentComponents.restore();
+      stringUtils.getFormattedStringFromArray.restore();
+      this.mock.restore();
+    });
+
+    it("isManualKerberos false, single master", function() {
+      var expected = Em.I18n.t('installer.step7.assign.master.body')
+        .format('c1', Em.I18n.t('common.host').toLowerCase(), Em.I18n.t('it')) +
+        '<br/>' + Em.I18n.t('installer.step7.assign.master.dependent.component.body').format('');
+      view.set('controller.mastersToCreate', ['c1']);
+      this.mock.returns(false);
+
+      view.setAlertMessage();
+      expect(view.get('alertMessage')).to.be.equal(expected);
+    });
+
+    it("isManualKerberos false, multiple masters", function() {
+      var expected = Em.I18n.t('installer.step7.assign.master.body')
+          .format('c1,c2', Em.I18n.t('common.hosts').toLowerCase(), Em.I18n.t('then')) +
+        '<br/>' + Em.I18n.t('installer.step7.assign.master.dependent.component.body').format('');
+      view.set('controller.mastersToCreate', ['c1', 'c2']);
+      this.mock.returns(false);
+
+      view.setAlertMessage();
+      expect(view.get('alertMessage')).to.be.equal(expected);
+    });
+
+    it("isManualKerberos true, single master", function() {
+      var expected = Em.I18n.t('installer.step7.assign.master.body')
+          .format('c1', Em.I18n.t('common.host').toLowerCase(), Em.I18n.t('it')) +
+        '<br/>' + Em.I18n.t('installer.step7.assign.master.dependent.component.body').format('') +
+        '<br/>' + Em.I18n.t('common.warn.message').format(Em.I18n.t('common.important') + ': ' + Em.I18n.t('installer.step8.kerberors.warning'));
+
+      view.set('controller.mastersToCreate', ['c1']);
+      this.mock.returns(true);
+
+      view.setAlertMessage();
+      expect(view.get('alertMessage')).to.be.equal(expected);
+    });
+  });
+
+});
\ No newline at end of file