You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2014/04/08 15:54:46 UTC

[2/2] git commit: AMBARI-5391. Unit tests for steps 0-2. (onechiporenko)

AMBARI-5391. Unit tests for steps 0-2. (onechiporenko)


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

Branch: refs/heads/trunk
Commit: de1a98410b60c65922e6221fa6c3a304217cd3f1
Parents: b6ef1f1
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Tue Apr 8 16:54:18 2014 +0300
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Tue Apr 8 16:54:18 2014 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   2 +
 .../app/controllers/wizard/step0_controller.js  |  23 +-
 .../app/controllers/wizard/step1_controller.js  |   2 +
 .../app/controllers/wizard/step2_controller.js  | 289 +++++++++-----
 ambari-web/app/mixins/common/localStorage.js    |   4 +-
 ambari-web/app/templates/wizard/step0.hbs       |   2 +-
 ambari-web/app/templates/wizard/step1.hbs       | 101 ++---
 ambari-web/app/templates/wizard/step2.hbs       |  70 ++--
 ambari-web/app/views/wizard/step0_view.js       |  29 +-
 ambari-web/app/views/wizard/step1_view.js       | 221 +++++------
 ambari-web/app/views/wizard/step2_view.js       |  69 +++-
 ambari-web/test/installer/step0_test.js         |  81 ++--
 ambari-web/test/installer/step2_test.js         | 292 +++++++++++++--
 ambari-web/test/views/wizard/step0_view_test.js |  43 +++
 ambari-web/test/views/wizard/step1_view_test.js | 374 ++++++++++++-------
 ambari-web/test/views/wizard/step2_view_test.js |  41 ++
 16 files changed, 1129 insertions(+), 514 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/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 e739131..f4a2ea1 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -112,7 +112,9 @@ require('test/views/main/charts/heatmap/heatmap_host_test');
 require('test/views/main/charts/heatmap/heatmap_rack_test');
 require('test/views/main/service/info/config_test');
 require('test/views/common/configs/services_config_test');
+require('test/views/wizard/step0_view_test');
 require('test/views/wizard/step1_view_test');
+require('test/views/wizard/step2_view_test');
 require('test/views/wizard/step3_view_test');
 require('test/views/wizard/step9_view_test');
 require('test/models/host_test');

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/controllers/wizard/step0_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step0_controller.js b/ambari-web/app/controllers/wizard/step0_controller.js
index fbf167e..dd7935d 100644
--- a/ambari-web/app/controllers/wizard/step0_controller.js
+++ b/ambari-web/app/controllers/wizard/step0_controller.js
@@ -19,19 +19,20 @@
 var App = require('app');
 
 App.WizardStep0Controller = Em.Controller.extend({
+
   name: 'wizardStep0Controller',
 
-  hasSubmitted : false,
+  /**
+   * Is step submitted
+   * @type {bool}
+   */
+  hasSubmitted: false,
 
-  loadStep: function () {
-    this.set('hasSubmitted',false);
-    this.set('clusterNameError', '');
-  },
   /**
    * validate cluster name
-   * set clusterNameError if validation fails
+   * set <code>clusterNameError</code> if validation fails
    */
-  invalidClusterName : function(){
+  invalidClusterName: function () {
     var clusterName = this.get('content.cluster.name');
     if (clusterName == '' && this.get('hasSubmitted')) {
       this.set('clusterNameError', Em.I18n.t('installer.step0.clusterName.error.required'));
@@ -50,12 +51,18 @@ App.WizardStep0Controller = Em.Controller.extend({
 
   /**
    * calculates by <code>invalidClusterName</code> property
+   * todo: mix this and previous variables in one
    */
-  //todo: mix this and previous variables in one
   clusterNameError: '',
 
+  loadStep: function () {
+    this.set('hasSubmitted', false);
+    this.set('clusterNameError', '');
+  },
+
   /**
    * Onclick handler for <code>next</code> button
+   * @method submit
    */
   submit: function () {
     this.set('hasSubmitted', true);

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/controllers/wizard/step1_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step1_controller.js b/ambari-web/app/controllers/wizard/step1_controller.js
index ebcf1e0..0ae3165 100644
--- a/ambari-web/app/controllers/wizard/step1_controller.js
+++ b/ambari-web/app/controllers/wizard/step1_controller.js
@@ -19,5 +19,7 @@
 var App = require('app');
 
 App.WizardStep1Controller = Em.Controller.extend({
+
   name: 'wizardStep1Controller'
+
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/controllers/wizard/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step2_controller.js b/ambari-web/app/controllers/wizard/step2_controller.js
index babdf31..4631d16 100644
--- a/ambari-web/app/controllers/wizard/step2_controller.js
+++ b/ambari-web/app/controllers/wizard/step2_controller.js
@@ -21,71 +21,161 @@ var validator = require('utils/validator');
 var lazyloading = require('utils/lazy_loading');
 
 App.WizardStep2Controller = Em.Controller.extend({
+
   name: 'wizardStep2Controller',
+
+  /**
+   * List of not installed hostnames
+   * @type {string[]}
+   */
   hostNameArr: [],
+
+  /**
+   * Does pattern-expression for hostnames contains some errors
+   * @type {bool}
+   */
   isPattern: false,
-  bootRequestId:  null,
+
+  /**
+   * Don't know if it used any more
+   */
+  bootRequestId: null,
+
+  /**
+   * Is step submitted
+   * @type {bool}
+   */
   hasSubmitted: false,
+
+  /**
+   * @type {string[]}
+   */
   inputtedAgainHostNames: [],
 
+  /**
+   * Is Installer Controller used
+   * @type {bool}
+   */
   isInstaller: function () {
     return this.get('content.controllerName') == 'installerController';
   }.property('content.controllerName'),
 
+  /**
+   * "Shortcut" to <code>content.installOptions.hostNames</code>
+   * @type {string[]}
+   */
   hostNames: function () {
     return this.get('content.installOptions.hostNames');
   }.property('content.installOptions.hostNames'),
 
+  /**
+   * Is manual install selected
+   * "Shortcut" to <code>content.installOptions.manualInstall</code>
+   * @type {bool}
+   */
   manualInstall: function () {
     return this.get('content.installOptions.manualInstall');
   }.property('content.installOptions.manualInstall'),
 
+  /**
+   * "Shortcut" to <code>content.installOptions.sshKey</code>
+   * @type {string}
+   */
   sshKey: function () {
     return this.get('content.installOptions.sshKey');
   }.property('content.installOptions.sshKey'),
 
+  /**
+   * "Shortcut" to <code>content.installOptions.sshUser</code>
+   * @type {string}
+   */
   sshUser: function () {
     return this.get('content.installOptions.sshUser');
   }.property('content.installOptions.sshUser'),
 
+  /**
+   * Installed type based on <code>manualInstall</code>
+   * @type {string}
+   */
   installType: function () {
     return this.get('manualInstall') ? 'manualDriven' : 'ambariDriven';
   }.property('manualInstall'),
 
-  isHostNameValid: function (hostname) {
-    return validator.isHostname(hostname);
-  },
   /**
-   * set not installed hosts to the hostNameArr
+   * List of invalid hostnames
+   * @type {string[]}
+   */
+  invalidHostNames: [],
+
+  /**
+   * Error-message if <code>hostNames</code> is empty, null otherwise
+   * @type {string|null}
+   */
+  hostsError: null,
+
+  /**
+   * Error-message if <code>sshKey</code> is empty, null otherwise
+   * @type {string|null}
+   */
+  sshKeyError: function () {
+    if (this.get('hasSubmitted') && this.get('manualInstall') === false && Em.isEmpty(this.get('sshKey').trim())) {
+      return Em.I18n.t('installer.step2.sshKey.error.required');
+    }
+    return null;
+  }.property('sshKey', 'manualInstall', 'hasSubmitted'),
+
+  /**
+   * Error-message if <code>sshUser</code> is empty, null otherwise
+   * @type {string|null}
+   */
+  sshUserError: function () {
+    if (this.get('manualInstall') === false && Em.isEmpty(this.get('sshUser').trim())) {
+      return Em.I18n.t('installer.step2.sshUser.required');
+    }
+    return null;
+  }.property('sshUser', 'hasSubmitted', 'manualInstall'),
+
+  /**
+   * is Submit button disabled
+   * @type {bool}
+   */
+  isSubmitDisabled: function () {
+    return (this.get('hostsError') || this.get('sshKeyError') || this.get('sshUserError'));
+  }.property('hostsError', 'sshKeyError', 'sshUserError'),
+
+  /**
+   * Set not installed hosts to the hostNameArr
+   * @method updateHostNameArr
    */
-  updateHostNameArr: function(){
-    this.hostNameArr = this.get('hostNames').trim().split(new RegExp("\\s+", "g"));
-    this.patternExpression();
+  updateHostNameArr: function () {
+    this.set('hostNameArr', this.get('hostNames').trim().split(new RegExp("\\s+", "g")));
+    this.parseHostNamesAsPatternExpression();
     this.get('inputtedAgainHostNames').clear();
-    var installedHostNames = App.Host.find().mapProperty('hostName');
-    var tempArr = [];
-    for (var i = 0; i < this.hostNameArr.length; i++) {
-      if (!installedHostNames.contains(this.hostNameArr[i])) {
-        tempArr.push(this.hostNameArr[i]);
-      } else {
-        this.get('inputtedAgainHostNames').push(this.hostNameArr[i]);
+    var installedHostNames = App.Host.find().mapProperty('hostName'),
+      tempArr = [],
+      hostNameArr = this.get('hostNameArr');
+    for (var i = 0; i < hostNameArr.length; i++) {
+      if (!installedHostNames.contains(hostNameArr[i])) {
+        tempArr.push(hostNameArr[i]);
+      }
+      else {
+        this.get('inputtedAgainHostNames').push(hostNameArr[i]);
       }
     }
     this.set('hostNameArr', tempArr);
   },
 
-  invalidHostNames: [],
-
   /**
-   * validate host names
-   * @return {Boolean}
+   * Validate host names
+   * @method isAllHostNamesValid
+   * @return {bool}
    */
   isAllHostNamesValid: function () {
     var result = true;
     this.updateHostNameArr();
     this.get('invalidHostNames').clear();
-    this.hostNameArr.forEach(function(hostName){
-      if (!this.isHostNameValid(hostName)) {
+    this.get('hostNameArr').forEach(function (hostName) {
+      if (!validator.isHostname(hostName)) {
         this.get('invalidHostNames').push(hostName);
         result = false;
       }
@@ -94,12 +184,12 @@ App.WizardStep2Controller = Em.Controller.extend({
     return result;
   },
 
-  hostsError: null,
   /**
-   * set hostsError if host names don't pass validation
+   * Set hostsError if host names don't pass validation
+   * @method checkHostError
    */
   checkHostError: function () {
-    if (this.get('hostNames').trim() === '') {
+    if (Em.isEmpty(this.get('hostNames').trim())) {
       this.set('hostsError', Em.I18n.t('installer.step2.hostName.error.required'));
     }
     else {
@@ -107,28 +197,19 @@ App.WizardStep2Controller = Em.Controller.extend({
     }
   },
 
-  checkHostAfterSubmitHandler: function() {
+  /**
+   * Check hostnames after Submit was clicked or <code>hostNames</code> were changed
+   * @method checkHostAfterSubmitHandler
+   */
+  checkHostAfterSubmitHandler: function () {
     if (this.get('hasSubmitted')) {
       this.checkHostError();
     }
   }.observes('hasSubmitted', 'hostNames'),
 
-  sshKeyError: function () {
-    if (this.get('hasSubmitted') && this.get('manualInstall') === false && this.get('sshKey').trim() === '') {
-      return Em.I18n.t('installer.step2.sshKey.error.required');
-    }
-    return null;
-  }.property('sshKey', 'manualInstall', 'hasSubmitted'),
-
-  sshUserError: function(){
-    if (this.get('manualInstall') === false && this.get('sshUser').trim() === '') {
-      return Em.I18n.t('installer.step2.sshUser.required');
-    }
-    return null;
-  }.property('sshUser', 'hasSubmitted', 'manualInstall'),
-
   /**
    * Get host info, which will be saved in parent controller
+   * @method getHostInfo
    */
   getHostInfo: function () {
 
@@ -147,16 +228,18 @@ App.WizardStep2Controller = Em.Controller.extend({
 
   /**
    * Used to set sshKey from FileUploader
-   * @param sshKey
+   * @method setSshKey
+   * @param {string} sshKey
    */
-  setSshKey: function(sshKey){
+  setSshKey: function (sshKey) {
     this.set("content.installOptions.sshKey", sshKey);
   },
 
   /**
    * Onclick handler for <code>next button</code>. Do all UI work except data saving.
    * This work is doing by router.
-   * @return {Boolean}
+   * @method evaluateStep
+   * @return {bool}
    */
   evaluateStep: function () {
     console.log('TRACE: Entering controller:WizardStep2:evaluateStep function');
@@ -174,65 +257,70 @@ App.WizardStep2Controller = Em.Controller.extend({
 
     this.updateHostNameArr();
 
-    if (!this.hostNameArr.length) {
+    if (!this.get('hostNameArr.length')) {
       this.set('hostsError', Em.I18n.t('installer.step2.hostName.error.already_installed'));
       return false;
     }
 
-    if(this.isPattern)
-    {
-      this.hostNamePatternPopup(this.hostNameArr);
+    if (this.get('isPattern')) {
+      this.hostNamePatternPopup(this.get('hostNameArr'));
       return false;
     }
-    if (this.get('inputtedAgainHostNames').length) {
+    if (this.get('inputtedAgainHostNames.length')) {
       this.installedHostsPopup();
-    } else {
+    }
+    else {
       this.proceedNext();
     }
+    return true;
   },
+
   /**
    * check is there a pattern expression in host name textarea
    * push hosts that match pattern in hostNamesArr
+   * @method parseHostNamesAsPatternExpression
    */
-  patternExpression: function(){
-    this.isPattern = false;
+  parseHostNamesAsPatternExpression: function () {
+    this.set('isPattern', false);
     var self = this;
     var hostNames = [];
-    $.each(this.hostNameArr, function(e,a){
-      var start, end, extra = {0:""};
-      if(/\[\d*\-\d*\]/.test(a)){
-        start=a.match(/\[\d*/);
-        end=a.match(/\-\d*]/);
+    $.each(this.get('hostNameArr'), function (e, a) {
+      var start, end, extra = {0: ""};
+      if (/\[\d*\-\d*\]/.test(a)) {
+        start = a.match(/\[\d*/);
+        end = a.match(/\-\d*]/);
 
-        start=start[0].substr(1);
-        end=end[0].substr(1);
+        start = start[0].substr(1);
+        end = end[0].substr(1);
 
-        if(parseInt(start) <= parseInt(end, 10) && parseInt(start, 10) >= 0){
-          self.isPattern = true;
+        if (parseInt(start) <= parseInt(end, 10) && parseInt(start, 10) >= 0) {
+          self.set('isPattern', true);
 
-          if(start[0] == "0" && start.length > 1) {
+          if (start[0] == "0" && start.length > 1) {
             extra = start.match(/0*/);
           }
 
           for (var i = parseInt(start, 10); i < parseInt(end, 10) + 1; i++) {
-            hostNames.push(a.replace(/\[\d*\-\d*\]/,extra[0].substring(0,start.length-i.toString().length)+i))
+            hostNames.push(a.replace(/\[\d*\-\d*\]/, extra[0].substring(0, start.length - i.toString().length) + i))
           }
 
-        }else{
+        } else {
           hostNames.push(a);
         }
-      }else{
+      } else {
         hostNames.push(a);
       }
     });
-    this.hostNameArr =  hostNames;
+    this.set('hostNameArr', hostNames);
   },
+
   /**
    * launch hosts to bootstrap
    * and save already registered hosts
-   * @return {Boolean}
+   * @method proceedNext
+   * @return {bool}
    */
-  proceedNext: function(warningConfirmed){
+  proceedNext: function (warningConfirmed) {
     if (this.isAllHostNamesValid() !== true && !warningConfirmed) {
       this.warningPopup();
       return false;
@@ -245,7 +333,7 @@ App.WizardStep2Controller = Em.Controller.extend({
 
     var bootStrapData = JSON.stringify({'verbose': true, 'sshKey': this.get('sshKey'), 'hosts': this.get('hostNameArr'), 'user': this.get('sshUser')});
 
-    if (App.skipBootstrap) {
+    if (App.get('skipBootstrap')) {
       this.saveHosts();
       return true;
     }
@@ -258,10 +346,12 @@ App.WizardStep2Controller = Em.Controller.extend({
       this.set('content.installOptions.bootRequestId', requestId);
       this.saveHosts();
     }
+    return true;
   },
 
   /**
    * show warning for host names without dots or IP addresses
+   * @method warningPopup
    */
   warningPopup: function () {
     var self = this;
@@ -271,14 +361,15 @@ App.WizardStep2Controller = Em.Controller.extend({
         this.hide();
         self.proceedNext(true);
       },
-      bodyClass: Ember.View.extend({
-        template: Ember.Handlebars.compile(Em.I18n.t('installer.step2.warning.popup.body').format(self.get('invalidHostNames').join(', ')))
+      bodyClass: Em.View.extend({
+        template: Em.Handlebars.compile(Em.I18n.t('installer.step2.warning.popup.body').format(self.get('invalidHostNames').join(', ')))
       })
     });
   },
 
   /**
    * show popup with the list of hosts that are already part of the cluster
+   * @method installedHostsPopup
    */
   installedHostsPopup: function () {
     var self = this;
@@ -288,8 +379,8 @@ App.WizardStep2Controller = Em.Controller.extend({
         self.proceedNext();
         this.hide();
       },
-      bodyClass: Ember.View.extend({
-        inputtedAgainHostNames: function() {
+      bodyClass: Em.View.extend({
+        inputtedAgainHostNames: function () {
           return self.get('inputtedAgainHostNames').join(', ');
         }.property(),
         templateName: require('templates/wizard/step2_installed_hosts_popup')
@@ -298,8 +389,9 @@ App.WizardStep2Controller = Em.Controller.extend({
   },
 
   /**
-   * show popup with hosts generated by pattern
-   * @param hostNames
+   * Show popup with hosts generated by pattern
+   * @method hostNamePatternPopup
+   * @param {string[]} hostNames
    */
   hostNamePatternPopup: function (hostNames) {
     var self = this;
@@ -309,11 +401,11 @@ App.WizardStep2Controller = Em.Controller.extend({
         self.proceedNext();
         this.hide();
       },
-      bodyClass: Ember.View.extend({
+      bodyClass: Em.View.extend({
         templateName: require('templates/common/items_list_popup'),
         items: hostNames,
         insertedItems: [],
-        didInsertElement: function() {
+        didInsertElement: function () {
           lazyloading.run({
             destination: this.get('insertedItems'),
             source: this.get('items'),
@@ -326,9 +418,11 @@ App.WizardStep2Controller = Em.Controller.extend({
       })
     });
   },
+
   /**
-   * show notify that installation is manual
+   * Show notify that installation is manual
    * save hosts
+   * @method manualInstallPopup
    */
   manualInstallPopup: function () {
     var self = this;
@@ -338,16 +432,18 @@ App.WizardStep2Controller = Em.Controller.extend({
         this.hide();
         self.saveHosts();
       },
-      bodyClass: Ember.View.extend({
+      bodyClass: Em.View.extend({
         templateName: require('templates/wizard/step2ManualInstallPopup')
       })
     });
   },
+
   /**
-   * warn to manually install ambari-agent on each host
+   * Warn to manually install ambari-agent on each host
+   * @method manualInstallWarningPopup
    */
-  manualInstallWarningPopup: function(){
-    if(!this.get('content.installOptions.useSsh')){
+  manualInstallWarningPopup: function () {
+    if (!this.get('content.installOptions.useSsh')) {
       App.ModalPopup.show({
         header: Em.I18n.t('common.warning'),
         body: Em.I18n.t('installer.step2.manualInstall.info'),
@@ -358,11 +454,11 @@ App.WizardStep2Controller = Em.Controller.extend({
     this.set('content.installOptions.manualInstall', !this.get('content.installOptions.useSsh'));
   }.observes('content.installOptions.useSsh'),
 
-  isSubmitDisabled: function () {
-    return (this.get('hostsError') || this.get('sshKeyError') || this.get('sshUserError'))  ;
-  }.property('hostsError', 'sshKeyError', 'sshUserError'),
-
-  setAmbariJavaHome: function(){
+  /**
+   * Load java.home value frin server
+   * @method setAmbariJavaHome
+   */
+  setAmbariJavaHome: function () {
     App.ajax.send({
       name: 'ambari.service',
       sender: this,
@@ -371,16 +467,29 @@ App.WizardStep2Controller = Em.Controller.extend({
     });
   },
 
-  onGetAmbariJavaHomeSuccess: function(data) {
-    this.set('content.installOptions.javaHome',data.RootServiceComponents.properties['java.home']);
+  /**
+   * Set received java.home value
+   * @method onGetAmbariJavaHomeSuccess
+   * @param {Object} data
+   */
+  onGetAmbariJavaHomeSuccess: function (data) {
+    this.set('content.installOptions.javaHome', data.RootServiceComponents.properties['java.home']);
   },
 
-  onGetAmbariJavaHomeError: function() {
+  /**
+   * Set default java.home value
+   * @method onGetAmbariJavaHomeError
+   */
+  onGetAmbariJavaHomeError: function () {
     console.warn('can\'t get java.home value from server');
-    this.set('content.installOptions.javaHome',App.defaultJavaHome);
+    this.set('content.installOptions.javaHome', App.get('defaultJavaHome'));
   },
 
-  saveHosts: function(){
+  /**
+   * Save hosts info and proceed to the next step
+   * @method saveHosts
+   */
+  saveHosts: function () {
     this.set('content.hosts', this.getHostInfo());
     this.setAmbariJavaHome();
     App.router.send('next');

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/mixins/common/localStorage.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/localStorage.js b/ambari-web/app/mixins/common/localStorage.js
index ed78f9a..9e5dda8 100644
--- a/ambari-web/app/mixins/common/localStorage.js
+++ b/ambari-web/app/mixins/common/localStorage.js
@@ -20,7 +20,7 @@ var App = require('app');
 
 /**
  * Fast save/load value to Local Storage
- * Object that impements it should have property <code>name</code> defined
+ * Object that implements it should have property <code>name</code> defined
  * @type {Ember.Mixin}
  */
 App.LocalStorage = Em.Mixin.create({
@@ -55,4 +55,4 @@ App.LocalStorage = Em.Mixin.create({
     App.db.set(this.get('dbNamespace'), key, value);
   }
 
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/templates/wizard/step0.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step0.hbs b/ambari-web/app/templates/wizard/step0.hbs
index 78b5a9b..5beb63d 100644
--- a/ambari-web/app/templates/wizard/step0.hbs
+++ b/ambari-web/app/templates/wizard/step0.hbs
@@ -25,7 +25,7 @@
     <a href="javascript:void(null)"
        rel="popover"
       {{translateAttr title="installer.step0.clusterName.tooltip.title"
-       data-content="installer.step0.clusterName.tooltip.content"}}>{{t common.learnMore}}</a>
+      data-content="installer.step0.clusterName.tooltip.content"}}>{{t common.learnMore}}</a>
   </label>
 
   <div class="controls">

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/templates/wizard/step1.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step1.hbs b/ambari-web/app/templates/wizard/step1.hbs
index 99df714..c0ad822 100644
--- a/ambari-web/app/templates/wizard/step1.hbs
+++ b/ambari-web/app/templates/wizard/step1.hbs
@@ -27,19 +27,19 @@
 </form>
 
 {{#if App.supports.localRepositories}}
-<div class="accordion" id="advancedRepoAccordion">
-  <div class="accordion-group">
-    <div class="accordion-heading" {{action "onToggleBlock" target="view"}}>
-      <i {{bindAttr class=":pull-left :accordion-toggle view.isRLCollapsed:icon-caret-right:icon-caret-down"}}></i>
-      <a class="accordion-toggle">
-        {{t installer.step1.advancedRepo.title}}
-        {{#if view.isSubmitDisabled}}
-          <span class="badge badge-important">{{view.totalErrorCnt}}</span>
-        {{/if}}
-      </a>
+  <div class="accordion" id="advancedRepoAccordion">
+    <div class="accordion-group">
+      <div class="accordion-heading" {{action "onToggleBlock" target="view"}}>
+        <i {{bindAttr class=":pull-left :accordion-toggle view.isRLCollapsed:icon-caret-right:icon-caret-down"}}></i>
+        <a class="accordion-toggle">
+          {{t installer.step1.advancedRepo.title}}
+          {{#if view.isSubmitDisabled}}
+            <span class="badge badge-important">{{view.totalErrorCnt}}</span>
+          {{/if}}
+        </a>
 
-    </div>
-      <div  class="accordion-body collapse in">
+      </div>
+      <div class="accordion-body collapse in">
         <div class="accordion-inner">
           <div class="alert alert-info">{{t installer.step1.advancedRepo.message}}</div>
           <div class="alert alert-warning">{{t installer.step1.advancedRepo.importantMassage}}</div>
@@ -56,48 +56,49 @@
             </thead>
             <tbody>
               {{#each repoGroup in view.allRepositoriesGroup}}
-                <tr>
-                  <td class="group-checkbox">{{view Ember.Checkbox checkedBinding="repoGroup.checked"}}</td>
-                  <td class="os-td">
-                    <table {{bindAttr class="repoGroup.checked::disabled-label"}}>
-                      <tbody>
+              <tr>
+                <td class="group-checkbox">{{view Ember.Checkbox checkedBinding="repoGroup.checked"}}</td>
+                <td class="os-td">
+                  <table {{bindAttr class="repoGroup.checked::disabled-label"}}>
+                    <tbody>
                       {{#each repo in repoGroup}}
-                        <tr>
-                          <td  {{bindAttr class=":os repo.empty-error:label-error repo.invalid-error:label-error "}}>{{repo.osType}}</td>
-                        </tr>
+                      <tr>
+                        <td  {{bindAttr class=":os repo.empty-error:label-error repo.invalid-error:label-error "}}>{{repo.osType}}</td>
+                      </tr>
                       {{/each}}
-                      </tbody>
-                    </table>
-                  </td>
-                  <td class="validation-results">
-                    {{#if repoGroup.validation}}
-                      {{view view.popoverView repoGroupBinding="repoGroup"}}
-                    {{/if}}
-                  </td>
-                  <td  {{bindAttr class=":url-results repoGroup.checked::disabled-textfield repoGroup.empty-error:textfield-error repoGroup.invalid-error:textfield-error"}}>
-                    {{view Ember.TextField valueBinding="repoGroup.baseUrl"}}
-                  </td>
-                  <td class="clearAll-icon">
-                    {{#if repoGroup.clearAll}}
-                      <a {{action "clearGroupLocalRepository" repoGroup target="view" }}>
-                        <i class="icon-remove-sign"></i>
-                      </a>
-                    {{/if}}
-                  </td>
-                  <td class="action-results">
-                    {{#if repoGroup.undo}}
-                      <a {{action "undoGroupLocalRepository" repoGroup target="view" }}>
-                        <i class="icon-undo"></i>{{t common.undo}}
-                      </a>
-                    {{/if}}
-                  </td>
-                </tr>
+                    </tbody>
+                  </table>
+                </td>
+                <td class="validation-results">
+                  {{#if repoGroup.validation}}
+                    {{view view.popoverView repoGroupBinding="repoGroup"}}
+                  {{/if}}
+                </td>
+                <td  {{bindAttr class=":url-results repoGroup.checked::disabled-textfield repoGroup.empty-error:textfield-error repoGroup.invalid-error:textfield-error"}}>
+                  {{view Ember.TextField valueBinding="repoGroup.baseUrl"}}
+                </td>
+                <td class="clearAll-icon">
+                  {{#if repoGroup.clearAll}}
+                    <a {{action "clearGroupLocalRepository" repoGroup target="view" }}>
+                      <i class="icon-remove-sign"></i>
+                    </a>
+                  {{/if}}
+                </td>
+                <td class="action-results">
+                  {{#if repoGroup.undo}}
+                    <a {{action "undoGroupLocalRepository" repoGroup target="view" }}>
+                      <i class="icon-undo"></i>{{t common.undo}}
+                    </a>
+                  {{/if}}
+                </td>
+              </tr>
               {{/each}}
             </tbody>
           </table>
           <div id="skip-validation">
             <label>{{view Ember.Checkbox checkedBinding="view.skipValidationChecked" class="checkbox"}}{{t installer.step1.advancedRepo.skipValidation.message}}
-              <i class="icon-question-sign" rel="skip-validation-tooltip" data-toggle="tooltip" {{translateAttr title="installer.step1.advancedRepo.skipValidation.tooltip"}}></i></label>
+              <i class="icon-question-sign" rel="skip-validation-tooltip"
+                 data-toggle="tooltip" {{translateAttr title="installer.step1.advancedRepo.skipValidation.tooltip"}}></i></label>
           </div>
           {{#if view.emptyRepoExist}}
             <div class="alert">{{t installer.step1.attentionNeeded}}</div>
@@ -110,13 +111,13 @@
           {{/if}}
         </div>
       </div>
+    </div>
   </div>
-</div>
 {{/if}}
 
 <a class="btn pull-left" {{action back}}>&larr; {{t common.back}}</a>
 {{#if view.isSubmitDisabled}}
-   <a class="btn btn-success pull-right" disabled="disabled">{{t common.next}} &rarr;</a>
+  <a class="btn btn-success pull-right" disabled="disabled">{{t common.next}} &rarr;</a>
 {{else}}
-   <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>
 {{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/templates/wizard/step2.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step2.hbs b/ambari-web/app/templates/wizard/step2.hbs
index 3270c5d..553c8e3 100644
--- a/ambari-web/app/templates/wizard/step2.hbs
+++ b/ambari-web/app/templates/wizard/step2.hbs
@@ -36,7 +36,7 @@
       <div class="controls">
         {{view Ember.TextArea class="span6" valueBinding="content.installOptions.hostNames" rows="5" placeholder="host names"}}
         {{#if hostsError}}
-        <p class="help-inline">{{hostsError}}</p>
+          <p class="help-inline">{{hostsError}}</p>
         {{/if}}
       </div>
     </div>
@@ -49,36 +49,38 @@
       <label class="radio">
         {{view view.providingSSHKeyRadioButton}}
         {{t installer.step2.useSsh.provide}}
-          <a href="javascript:void(null)"
-             rel="popover"
-            {{translateAttr title="installer.step2.useSsh.tooltip.title" data-content="installer.step2.useSsh.tooltip.content"}}>
-            {{t installer.step2.useSsh.tooltip.title}}</a>
+        <a href="javascript:void(null)"
+           rel="popover"
+          {{translateAttr title="installer.step2.useSsh.tooltip.title" data-content="installer.step2.useSsh.tooltip.content"}}>
+          {{t installer.step2.useSsh.tooltip.title}}</a>
         {{t installer.step2.useSsh.provide_id_rsa}}
       </label>
+
       <div class="ssh-key-input">
         {{#if view.isFileApi}}
           {{view App.SshKeyFileUploader disabledBinding="view.sshKeyState"}}
         {{/if}}
         <div {{bindAttr class="sshKeyError:error :controls :control-group"}}>
-            {{view Ember.TextArea class="span6" rows="3" id="sshKey"
-            placeholder="ssh private key" disabledBinding="view.sshKeyState" valueBinding="content.installOptions.sshKey"}}
-            {{#if sshKeyError}}
-                <span class="help-inline">{{sshKeyError}}</span>
-            {{/if}}
+          {{view Ember.TextArea class="span6" rows="3" id="sshKey"
+          placeholder="ssh private key" disabledBinding="view.sshKeyState" valueBinding="content.installOptions.sshKey"}}
+          {{#if sshKeyError}}
+            <span class="help-inline">{{sshKeyError}}</span>
+          {{/if}}
         </div>
         <div>
-            <label class="ssh-user pull-left">
-              {{t installer.step2.sshUser}}
-                <a href="javascript:void(null)"
-                   rel="popover"
-                  {{translateAttr title="installer.step2.sshUser.link" data-content="installer.step2.sshUser.toolTip"}}>
-                  {{t installer.step2.sshUser.link}}</a>
-              {{t installer.step2.sshUser.account}}
-            </label>
+          <label class="ssh-user pull-left">
+            {{t installer.step2.sshUser}}
+            <a href="javascript:void(null)"
+               rel="popover"
+              {{translateAttr title="installer.step2.sshUser.link" data-content="installer.step2.sshUser.toolTip"}}>
+              {{t installer.step2.sshUser.link}}</a>
+            {{t installer.step2.sshUser.account}}
+          </label>
+
           <div {{bindAttr class="sshUserError:error :control-group"}}>
             {{view view.textFieldView valueBinding="content.installOptions.sshUser" isEnabledBinding="content.installOptions.useSsh"}}
             {{#if sshUserError}}
-                <span class="help-inline">{{sshUserError}}</span>
+              <span class="help-inline">{{sshUserError}}</span>
             {{/if}}
           </div>
         </div>
@@ -87,10 +89,10 @@
       <label class="radio">
         {{view view.manualRegistrationRadioButton}}
         {{t installer.step2.manualInstall.perform}}
-          <a href="javascript:void(null)"
-             rel="popover"
-            {{translateAttr title="installer.step2.manualInstall.tooltip.title" data-content="installer.step2.manualInstall.tooltip.content"}}>
-            {{t installer.step2.manualInstall.tooltip.title}}</a>
+        <a href="javascript:void(null)"
+           rel="popover"
+          {{translateAttr title="installer.step2.manualInstall.tooltip.title" data-content="installer.step2.manualInstall.tooltip.content"}}>
+          {{t installer.step2.manualInstall.tooltip.title}}</a>
         {{t installer.step2.manualInstall.perform_on_hosts}}
       </label>
 
@@ -99,23 +101,23 @@
 
   <div class="advancedOptions">
     {{#unless App.supports.localRepositories}}
-    <h5>{{t installer.step2.advancedOptions.header}}</h5>
-    <label {{bindAttr class=":checkbox"}}>
-      {{view Ember.Checkbox checkedBinding="content.installOptions.localRepo"}}
+      <h5>{{t installer.step2.advancedOptions.header}}</h5>
+      <label {{bindAttr class=":checkbox"}}>
+        {{view Ember.Checkbox checkedBinding="content.installOptions.localRepo"}}
 
-      {{t installer.step2.localRepo.label_use}}
+        {{t installer.step2.localRepo.label_use}}
 
-      <a href="javascript:void(null)"
-         rel="popover"
-        {{translateAttr title="installer.step2.localRepo.tooltip.title" data-content="installer.step2.localRepo.tooltip.content"}}>
-        {{t installer.step2.localRepo.tooltip.title}}</a>
-      {{t installer.step2.localRepo.label_instead}}
-    </label>
+        <a href="javascript:void(null)"
+           rel="popover"
+          {{translateAttr title="installer.step2.localRepo.tooltip.title" data-content="installer.step2.localRepo.tooltip.content"}}>
+          {{t installer.step2.localRepo.tooltip.title}}</a>
+        {{t installer.step2.localRepo.label_instead}}
+      </label>
     {{/unless}}
   </div>
   <div class="btn-area">
     {{#unless view.parentView.controller.hideBackButton}}
-    <a class="btn pull-left" {{action back}}>&larr; {{t common.back}}</a>
+      <a class="btn pull-left" {{action back}}>&larr; {{t common.back}}</a>
     {{/unless}}
     <a class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}} {{action evaluateStep target="controller"}}>
       {{t installer.step2.registerAndConfirm}} &rarr;</a>

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/views/wizard/step0_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step0_view.js b/ambari-web/app/views/wizard/step0_view.js
index f1de05c..07d3414 100644
--- a/ambari-web/app/views/wizard/step0_view.js
+++ b/ambari-web/app/views/wizard/step0_view.js
@@ -21,30 +21,49 @@ var App = require('app');
 
 App.WizardStep0View = Em.View.extend({
 
-  tagName: "form", //todo: why form?
+  tagName: "form",
+
   attributeBindings: ['autocomplete'],
+
+  /**
+   * Disable autocomplete for form's fields
+   */
   autocomplete: 'off',
-  templateName: require('templates/wizard/step0'),
 
-  //todo: create property for placeholder(go to template)
+  templateName: require('templates/wizard/step0'),
 
   didInsertElement: function () {
     App.popover($("[rel=popover]"), {'placement': 'right', 'trigger': 'hover'});
     this.get('controller').loadStep();
   },
 
-  //todo: rename it to class or write comments
+  /**
+   * Is some error with provided cluster name
+   * @type {bool}
+   */
   onError: function () {
-    return this.get('controller.clusterNameError') !== '';
+    return !Em.isEmpty(this.get('controller.clusterNameError'));
   }.property('controller.clusterNameError')
 
 });
 
+/**
+ * Field for cluster name
+ * @type {Ember.TextField}
+ */
 App.WizardStep0ViewClusterNameInput = Em.TextField.extend({
+
+  /**
+   * Submit form if "Enter" pressed
+   * @method keyPress
+   * @param {object} event
+   * @returns {bool}
+   */
   keyPress: function(event) {
     if (event.keyCode == 13) {
       this.get('parentView.controller').submit();
       return false;
     }
+    return true;
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/views/wizard/step1_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step1_view.js b/ambari-web/app/views/wizard/step1_view.js
index 5734a4d..ff76638 100644
--- a/ambari-web/app/views/wizard/step1_view.js
+++ b/ambari-web/app/views/wizard/step1_view.js
@@ -5,9 +5,9 @@
  * 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
@@ -26,54 +26,38 @@ App.WizardStep1View = Em.View.extend({
    * @type {Em.Object[]}
    */
   stacks: function () {
-    var stacks = [];
-    this.get('controller.content.stacks').forEach(function (stack) {
-      stacks.pushObject(Em.Object.create({
+    return this.get('controller.content.stacks').map(function (stack) {
+      return Em.Object.create({
         name: stack.get('name').replace('-', ' '),
         isSelected: stack.get('isSelected')
-      }));
+      });
     });
-    return stacks;
   }.property('controller.content.stacks.@each.isSelected'),
 
   /**
-   * Checkbox for each stack
-   * @type {Em.Checkbox}
-   */
-  stackRadioButton: Em.Checkbox.extend({
-    tagName: 'input',
-    attributeBindings: [ 'type', 'checked' ],
-    checked: function () {
-      return this.get('content.isSelected');
-    }.property('content.isSelected'),
-    type: 'radio',
-
-    click: function () {
-      this.get('controller.content.stacks').setEach('isSelected', false);
-      this.get('controller.content.stacks').findProperty('name', this.get('content.name').replace(' ', '-')).set('isSelected', true);
-    }
-  }),
-
-  /**
    * List of all repo-groups
    * @type {Object[][]}
    */
-  allRepositoriesGroup: [[],[],[]],
+  allRepositoriesGroup: [
+    [],
+    [],
+    []
+  ],
 
   /**
    * Verify if some repo has empty base-url
    * @type {bool}
    */
   emptyRepoExist: function () {
-    return (this.get('allRepositoriesGroup').filterProperty('empty-error', true).length != 0);
+    return this.get('allRepositoriesGroup').someProperty('empty-error', true);
   }.property('allRepositoriesGroup.@each.empty-error'),
 
   /**
    * Disable submit button flag
    * @type {bool}
    */
-  isSubmitDisabled: function() {
-    return this.get('emptyRepoExist') || this.get('allRepoUnchecked') || this.get('invalidUrlExist') ;
+  isSubmitDisabled: function () {
+    return this.get('emptyRepoExist') || this.get('allRepoUnchecked') || this.get('invalidUrlExist');
   }.property('emptyRepoExist', 'allRepoUnchecked', 'invalidUrlExist'),
 
   /**
@@ -82,7 +66,7 @@ App.WizardStep1View = Em.View.extend({
    */
   invalidUrlExist: function () {
     var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true);
-    var invalidExist = this.get('allRepositoriesGroup').filterProperty('validation', 'icon-exclamation-sign').length != 0;
+    var invalidExist = this.get('allRepositoriesGroup').someProperty('validation', 'icon-exclamation-sign');
     return (selectedStack.get('invalidCnt') > 0) && invalidExist;
   }.property('controller.content.stacks.@each.invalidCnt', 'allRepositoriesGroup.@each.validation'),
 
@@ -91,7 +75,7 @@ App.WizardStep1View = Em.View.extend({
    * @type {bool}
    */
   allRepoUnchecked: function () {
-    return (!this.get('allRepositoriesGroup').filterProperty('checked', true).length);
+    return !this.get('allRepositoriesGroup').someProperty('checked', true);
   }.property('allRepositoriesGroup.@each.checked'),
 
   /**
@@ -103,7 +87,7 @@ App.WizardStep1View = Em.View.extend({
     var invalidCnt = this.get('allRepositoriesGroup').filterProperty('validation', 'icon-exclamation-sign').length;
     if (this.get('allRepoUnchecked')) {
       return 1;
-    } else if ( emptyCnt || invalidCnt) {
+    } else if (emptyCnt || invalidCnt) {
       return emptyCnt + invalidCnt;
     } else {
       return 0;
@@ -111,18 +95,22 @@ App.WizardStep1View = Em.View.extend({
   }.property('allRepositoriesGroup.@each.empty-error', 'allRepoUnchecked', 'allRepositoriesGroup.@each.validation'),
 
   /**
-   * Onclick handler for Config Group Header. Used to show/hide block
+   * Is Repositories Accordion collapsed
+   * @type {bool}
    */
-  onToggleBlock: function () {
-    this.$('.accordion-body').toggle('blind', 500);
-    this.set('isRLCollapsed', !this.get('isRLCollapsed'));
-  },
+  isRLCollapsed: true,
 
   /**
-   * Is Repositories Accordion collapsed
+   * Checked flags for each repo-checkbox
+   * @type {bool[]}
+   */
+  allGroupsCheckbox: [true, true, true],
+
+  /**
+   * Skip repo-validation
    * @type {bool}
    */
-  isRLCollapsed: true,
+  skipValidationChecked: false,
 
   didInsertElement: function () {
     if (this.get('isRLCollapsed')) {
@@ -132,6 +120,24 @@ App.WizardStep1View = Em.View.extend({
   },
 
   /**
+   * Checkbox for each stack
+   * @type {Ember.Checkbox}
+   */
+  stackRadioButton: Em.Checkbox.extend({
+    tagName: 'input',
+    attributeBindings: [ 'type', 'checked' ],
+    checked: function () {
+      return this.get('content.isSelected');
+    }.property('content.isSelected'),
+    type: 'radio',
+
+    click: function () {
+      this.get('controller.content.stacks').setEach('isSelected', false);
+      this.get('controller.content.stacks').findProperty('name', this.get('content.name').replace(' ', '-')).set('isSelected', true);
+    }
+  }),
+
+  /**
    * Popover for repo-url error indicator
    * @type {Em.View}
    */
@@ -139,25 +145,39 @@ App.WizardStep1View = Em.View.extend({
     tagName: 'i',
     classNameBindings: ['repoGroup.validation'],
     attributeBindings: ['repoGroup.errorTitle:title', 'repoGroup.errorContent:data-content'],
-    didInsertElement: function() {
+    didInsertElement: function () {
       App.popover($(this.get('element')), {'trigger': 'hover'});
     }
   }),
 
   /**
+   * Onclick handler for Config Group Header. Used to show/hide block
+   * @method onToggleBlock
+   */
+  onToggleBlock: function () {
+    this.$('.accordion-body').toggle('blind', 500);
+    this.set('isRLCollapsed', !this.get('isRLCollapsed'));
+  },
+
+  /**
    * Format repo-group values and set it to <code>allRepositoriesGroup</code>
+   * @method loadRepositories
    */
   loadRepositories: function () {
     var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true);
-    var reposGroup = [[],[],[]];
-    if (App.supports.ubuntu) reposGroup.push([]); // @todo: remove after Ubuntu support confirmation
+    var reposGroup = [
+      [],
+      [],
+      []
+    ];
+    if (App.get('supports.ubuntu')) reposGroup.push([]); // @todo: remove after Ubuntu support confirmation
     var self = this;
     if (selectedStack && selectedStack.operatingSystems) {
       selectedStack.operatingSystems.forEach(function (os) {
         var cur_repo = Em.Object.create({
           baseUrl: os.baseUrl
         });
-        switch(os.osType) {
+        switch (os.osType) {
           case 'redhat5':
             cur_repo.set('osType', 'Red Hat 5');
             reposGroup[0][0] = cur_repo;
@@ -197,9 +217,11 @@ App.WizardStep1View = Em.View.extend({
             reposGroup[2][1] = cur_repo;
             break;
           case 'ubuntu12':
-            cur_repo.set('osType','Ubuntu 12');
-            reposGroup[3][0] = cur_repo;
-            self.setGroupByOs(reposGroup[3], os, 3);
+            if (App.get('supports.ubuntu')) {
+              cur_repo.set('osType', 'Ubuntu 12');
+              reposGroup[3][0] = cur_repo;
+              self.setGroupByOs(reposGroup[3], os, 3);
+            }
             break;
         }
       });
@@ -209,7 +231,8 @@ App.WizardStep1View = Em.View.extend({
 
   /**
    * Set group parameters according to operation system
-   * @param {Em.Object} group
+   * @method setGroupByOs
+   * @param {Ember.Object} group
    * @param {Object} os
    * @param {number} groupNumber
    */
@@ -231,6 +254,7 @@ App.WizardStep1View = Em.View.extend({
 
   /**
    * Onclick handler for checkbox of each repo group
+   * @method updateByCheckbox
    */
   updateByCheckbox: function () {
     //upload to content
@@ -251,7 +275,7 @@ App.WizardStep1View = Em.View.extend({
           targetGroup.set('invalid-error', false);
           targetGroup.set('validation', null);
           targetGroup.set('clearAll', false);
-          targetGroup.set('empty-error',!targetGroup.get('baseUrl'));
+          targetGroup.set('empty-error', !targetGroup.get('baseUrl'));
           self.get('allGroupsCheckbox')[groupNumber] = false;
           self.set('allGroupsCheckbox', self.get('allGroupsCheckbox'));
         } else {
@@ -262,7 +286,7 @@ App.WizardStep1View = Em.View.extend({
             targetGroup.set('invalid-error', false);
           }
           targetGroup.set('clearAll', targetGroup.get('baseUrl'));
-          targetGroup.set('empty-error',!targetGroup.get('baseUrl'));
+          targetGroup.set('empty-error', !targetGroup.get('baseUrl'));
           self.get('allGroupsCheckbox')[groupNumber] = true;
         }
       });
@@ -270,43 +294,35 @@ App.WizardStep1View = Em.View.extend({
   }.observes('allRepositoriesGroup.@each.checked', 'skipValidationChecked'),
 
   /**
-   * Checked flags for each repo-checkbox
-   * @type {bool[]}
-   */
-  allGroupsCheckbox: [true, true, true],
-
-  /**
-   * Skip repo-validation
-   * @type {bool}
-   */
-  skipValidationChecked: false,
-
-  /**
    * Onclick handler for undo action of each repo group
+   * @method undoGroupLocalRepository
+   * @param {object} event
    */
   undoGroupLocalRepository: function (event) {
-    var group = event.context;
-    var osTypes = this.groupToOsType(group.get('group-number'));
-    var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true);
-    osTypes.forEach( function (os) {
-      var cos = selectedStack.operatingSystems.findProperty('osType', os );
-      cos.baseUrl = cos.latestBaseUrl;
-      cos.validation = null;
-    });
-    this.loadRepositories();
+    this.doActionForGroupLocalRepository(event, 'latestBaseUrl');
   },
 
   /**
    * Handler for clear icon click
+   * @method clearGroupLocalRepository
    * @param {object} event
    */
   clearGroupLocalRepository: function (event) {
-    var group = event.context;
-    var osTypes = this.groupToOsType(group.get('group-number'));
+    this.doActionForGroupLocalRepository(event, '');
+  },
+
+  /**
+   * Common handler for repo groups actions
+   * @method doActionForGroupLocalRepository
+   * @param {object} event
+   * @param {string} newBaseUrlField
+   */
+  doActionForGroupLocalRepository: function (event, newBaseUrlField) {
+    var osTypes = this.groupToOsType(event.context.get('group-number'));
     var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true);
-    osTypes.forEach( function (os) {
-      var cos = selectedStack.operatingSystems.findProperty('osType', os );
-      cos.baseUrl = '';
+    osTypes.forEach(function (os) {
+      var cos = selectedStack.operatingSystems.findProperty('osType', os);
+      cos.baseUrl = Em.isEmpty(newBaseUrlField) ? '' : Em.get(cos, newBaseUrlField);
       cos.validation = null;
     });
     this.loadRepositories();
@@ -314,9 +330,9 @@ App.WizardStep1View = Em.View.extend({
 
   /**
    * Handler when editing any repo group BaseUrl
-   * @param {object} event
+   * @method editGroupLocalRepository
    */
-  editGroupLocalRepository: function (event) {
+  editGroupLocalRepository: function () {
     //upload to content
     var groups = this.get('allRepositoriesGroup');
     var self = this;
@@ -331,7 +347,7 @@ App.WizardStep1View = Em.View.extend({
           targetGroup.set('invalid-error', false);
           targetGroup.set('validation', null);
           targetGroup.set('clearAll', os.baseUrl);
-          targetGroup.set('empty-error',!targetGroup.get('baseUrl'));
+          targetGroup.set('empty-error', !targetGroup.get('baseUrl'));
         }
       });
     }
@@ -339,44 +355,37 @@ App.WizardStep1View = Em.View.extend({
 
   /**
    * Get list of OS for provided group number
+   * @method groupToOsType
    * @param {number} groupNumber
    * @returns {Array}
    */
   groupToOsType: function (groupNumber) {
-    switch (groupNumber) {
-      case 0:
-        return ['redhat5', 'centos5', 'oraclelinux5'];
-      case 1:
-        return ['redhat6', 'centos6', 'oraclelinux6'];
-      case 2:
-        return ['sles11', 'suse11'];
-      case 3:
-        return ['ubuntu12'];
-    }
-    return [];
+    return Em.getWithDefault({
+      '0': ['redhat5', 'centos5', 'oraclelinux5'],
+      '1': ['redhat6', 'centos6', 'oraclelinux6'],
+      '2': ['sles11', 'suse11'],
+      '3': ['ubuntu12']
+    }, groupNumber.toString(), []);
   },
 
   /**
    * Get group number for provided OS
-   * @param  {string} osType
+   * @method osTypeToGroup
+   * @param {string} osType
    * @returns {number}
    */
   osTypeToGroup: function (osType) {
-    switch(osType) {
-      case 'redhat5':
-      case 'centos5':
-      case 'oraclelinux5':
-        return 0;
-      case 'redhat6':
-      case 'centos6':
-      case 'oraclelinux6':
-        return 1;
-      case 'sles11':
-      case 'suse11':
-        return 2;
-      case 'ubuntu12':
-        return 3;
-    }
-    return -1;
+    return Em.getWithDefault({
+      'redhat5': 0,
+      'centos5': 0,
+      'oraclelinux5': 0,
+      'redhat6': 1,
+      'centos6': 1,
+      'oraclelinux6': 1,
+      'sles11': 2,
+      'suse11': 2,
+      'ubuntu12': 3
+    }, osType, -1);
   }
+
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/app/views/wizard/step2_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step2_view.js b/ambari-web/app/views/wizard/step2_view.js
index d0f5d20..d0133a3 100644
--- a/ambari-web/app/views/wizard/step2_view.js
+++ b/ambari-web/app/views/wizard/step2_view.js
@@ -19,21 +19,22 @@
 
 var App = require('app');
 
-App.SshKeyFileUploader = Ember.View.extend({
+App.SshKeyFileUploader = Em.View.extend({
   //TODO: rewrite it using tagName and attribute binding
   //TODO: rewrite it as independent component and place it somewhere in utils
   // alternative is to move it to App.WizardStep2View
-  template:Ember.Handlebars.compile('<input type="file" {{bindAttr disabled="view.disabled"}} />'),
+  template: Em.Handlebars.compile('<input type="file" {{bindAttr disabled="view.disabled"}} />'),
+
   classNames: ['ssh-key-input-indentation'],
 
   change: function (e) {
-    var self=this;
+    var self = this;
     if (e.target.files && e.target.files.length == 1) {
       var file = e.target.files[0];
       var reader = new FileReader();
 
-      reader.onload = (function(theFile) {
-        return function(e) {
+      reader.onload = (function () {
+        return function (e) {
           $('#sshKey').html(e.target.result);
           self.get("controller").setSshKey(e.target.result);
         };
@@ -41,6 +42,7 @@ App.SshKeyFileUploader = Ember.View.extend({
       reader.readAsText(file);
     }
   }
+
 });
 
 App.WizardStep2View = Em.View.extend({
@@ -48,30 +50,44 @@ App.WizardStep2View = Em.View.extend({
   templateName: require('templates/wizard/step2'),
 
   didInsertElement: function () {
-    //TODO: move it to separate function in Ember.View using reopenClass
     App.popover($("[rel=popover]"), {'placement': 'right', 'trigger': 'hover'});
-
     //todo: move them to conroller
-    this.set('controller.hostsError',null);
-    this.set('controller.sshKeyError',null);
+    this.set('controller.hostsError', null);
+    this.set('controller.sshKeyError', null);
   },
 
-  sshKeyState: function(){
+  /**
+   * Is manualInstall selected
+   * @type {bool}
+   */
+  sshKeyState: function () {
     return this.get("controller.content.installOptions.manualInstall");
   }.property("controller.content.installOptions.manualInstall"),
 
-  //TODO: incupsulate it inside of App.SshKeyFileUploader
+  /**
+   * Is File API available
+   * @type {bool}
+   * TODO: incupsulate it inside of App.SshKeyFileUploader
+   */
   isFileApi: function () {
-    return (window.File && window.FileReader && window.FileList) ? true : false ;
+    return window.File && window.FileReader && window.FileList;
   }.property(),
 
-  //TODO: replace next 2 properties with new one used in both places
-  providingSSHKeyRadioButton: Ember.Checkbox.extend({
+  /**
+   * Checkbox for activate SSH fields
+   * @type {Ember.Checkbox}
+   * TODO: replace next 2 properties with new one used in both places
+   */
+  providingSSHKeyRadioButton: Em.Checkbox.extend({
+
     tagName: 'input',
+
     attributeBindings: ['type', 'checked'],
+
     checked: function () {
       return this.get('controller.content.installOptions.useSsh');
     }.property('controller.content.installOptions.useSsh'),
+
     type: 'radio',
 
     click: function () {
@@ -80,13 +96,20 @@ App.WizardStep2View = Em.View.extend({
     }
   }),
 
-  manualRegistrationRadioButton: Ember.Checkbox.extend({
+  /**
+   * Checkbox for manual registration
+   * @type {Ember.Checkbox}
+   */
+  manualRegistrationRadioButton: Em.Checkbox.extend({
     tagName: 'input',
+
     attributeBindings: ['type', 'checked'],
+
+    type: 'radio',
+
     checked: function () {
       return this.get('controller.content.installOptions.manualInstall');
     }.property('controller.content.installOptions.manualInstall'),
-    type: 'radio',
 
     click: function () {
       this.set('controller.content.installOptions.manualInstall', true);
@@ -94,11 +117,21 @@ App.WizardStep2View = Em.View.extend({
     }
   }),
 
-  textFieldView: Ember.TextField.extend({
-    disabled: function(){
+  /**
+   * Textarea with ssh-key
+   * @type {Ember.TextField}
+   */
+  textFieldView: Em.TextField.extend({
+
+    /**
+     * Is textfield disabled
+     * @type {bool}
+     */
+    disabled: function () {
       return !this.get('isEnabled');
     }.property('isEnabled')
   })
+
 });
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/test/installer/step0_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/installer/step0_test.js b/ambari-web/test/installer/step0_test.js
index eb9b3d8..0c6a87a 100644
--- a/ambari-web/test/installer/step0_test.js
+++ b/ambari-web/test/installer/step0_test.js
@@ -17,45 +17,27 @@
  */
 
 var App = require('app');
-require('controllers/wizard/step1_controller');
-
-/*describe('App.InstallerStep1Controller', function () {
-
-  describe('#validateStep1()', function () {
-    it('should return false and sets invalidClusterName to true if cluster name is empty', function () {
-      var controller = App.InstallerStep1Controller.create();
-      controller.set('clusterName', '');
-      expect(controller.validateStep1()).to.equal(false);
-      expect(controller.get('invalidClusterName')).to.equal(true);
-    })
-    it('should return false and sets invalidClusterName to true if cluster name has whitespaces', function () {
-      var controller = App.InstallerStep1Controller.create();
-      controller.set('clusterName', 'My Cluster');
-      expect(controller.validateStep1()).to.equal(false);
-      expect(controller.get('invalidClusterName')).to.equal(true);
-    })
-    it('should return false and sets invalidClusterName to true if cluster name has special characters', function () {
-      var controller = App.InstallerStep1Controller.create();
-      controller.set('clusterName', 'my-cluster');
-      expect(controller.validateStep1()).to.equal(false);
-      expect(controller.get('invalidClusterName')).to.equal(true);
-    })
-    it('should return true, sets invalidClusterName to false if cluster name is valid', function () {
-      var controller = App.InstallerStep1Controller.create();
-      var clusterName = 'mycluster1';
-      controller.set('clusterName', clusterName);
-      expect(controller.validateStep1()).to.equal(true);
-      expect(controller.get('invalidClusterName')).to.equal(false);
-    })
-  })
-
-})*/
-
+require('models/cluster_states');
 require('controllers/wizard/step0_controller');
+var wizardStep0Controller;
+
+if (!App.router) {
+  App.router = Em.Object.create({});
+}
+App.router.set('send', Em.K);
 
 describe('App.WizardStep0Controller', function () {
 
-  var wizardStep0Controller = App.WizardStep0Controller.create();
+  beforeEach(function() {
+    wizardStep0Controller = App.WizardStep0Controller.create({content: {cluster: {}}});
+    sinon.stub(App.clusterStatus, 'set', Em.K);
+    sinon.spy(App.router, 'send');
+  });
+
+  afterEach(function() {
+    App.clusterStatus.set.restore();
+    App.router.send.restore();
+  });
 
   describe('#invalidClusterName', function () {
     it('should return true if no cluster name is present', function () {
@@ -73,5 +55,32 @@ describe('App.WizardStep0Controller', function () {
       wizardStep0Controller.set('content', {'cluster':{'name':'$cluster'}});
       expect(wizardStep0Controller.get('invalidClusterName')).to.equal(true);
     })
-  })
+  });
+
+  describe('#loadStep', function() {
+    it('should clear step data', function() {
+      wizardStep0Controller.loadStep();
+      expect(wizardStep0Controller.get('hasSubmitted')).to.equal(false);
+      expect(wizardStep0Controller.get('clusterNameError')).to.equal('');
+    });
+  });
+
+  describe('#submit', function() {
+    it('if cluster name is valid should proceed', function() {
+      wizardStep0Controller.set('content.cluster.name', 'tdk');
+      wizardStep0Controller.submit();
+      expect(wizardStep0Controller.get('content.cluster.status')).to.equal('PENDING');
+      expect(wizardStep0Controller.get('content.cluster.isCompleted')).to.equal(false);
+      expect(App.router.send.calledWith('next')).to.equal(true);
+      expect(App.clusterStatus.set.calledWith('clusterName', 'tdk')).to.equal(true);
+    });
+
+    it('if cluster name isn\'t valid shouldn\'t proceed', function() {
+      wizardStep0Controller.set('content.cluster.name', '@@@@');
+      wizardStep0Controller.submit();
+      expect(App.router.send.called).to.equal(false);
+      expect(App.clusterStatus.set.called).to.equal(false);
+    });
+  });
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/test/installer/step2_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/installer/step2_test.js b/ambari-web/test/installer/step2_test.js
index 20c1fc6..5be9c1d 100644
--- a/ambari-web/test/installer/step2_test.js
+++ b/ambari-web/test/installer/step2_test.js
@@ -25,6 +25,64 @@ require('messages');
 
 describe('App.WizardStep2Controller', function () {
 
+  describe('#isInstaller', function() {
+    it('true if controllerName is installerController', function() {
+      var controller = App.WizardStep2Controller.create({content: {controllerName: 'installerController'}});
+      expect(controller.get('isInstaller')).to.equal(true);
+    });
+    it('false if controllerName isn\'t installerController', function() {
+      var controller = App.WizardStep2Controller.create({content: {controllerName: 'addServiceController'}});
+      expect(controller.get('isInstaller')).to.equal(false);
+    });
+  });
+
+  describe('#manualInstall', function() {
+    it('should be equal to content.installOptions.manualInstall', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {manualInstall: true}}});
+      expect(controller.get('manualInstall')).to.equal(true);
+      controller.toggleProperty('content.installOptions.manualInstall');
+      expect(controller.get('manualInstall')).to.equal(false);
+    });
+  });
+
+  describe('#hostNames', function() {
+    it('should be equal to content.installOptions.hostNames', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {hostNames: ['1','2','3']}}});
+      expect(controller.get('hostNames')).to.eql(['1','2','3']);
+      controller.set('content.installOptions.hostNames', ['1', '2']);
+      expect(controller.get('hostNames')).to.eql(['1', '2']);
+    });
+  });
+
+  describe('#sshKey', function() {
+    it('should be equal to content.installOptions.sshKey', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {sshKey: '123'}}});
+      expect(controller.get('sshKey')).to.equal('123');
+      controller.set('content.installOptions.sshKey', '321');
+      expect(controller.get('sshKey')).to.equal('321');
+    });
+  });
+
+  describe('#sshUser', function() {
+    it('should be equal to content.installOptions.sshUser', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {sshUser: '123'}}});
+      expect(controller.get('sshUser')).to.equal('123');
+      controller.set('content.installOptions.sshUser', '321');
+      expect(controller.get('sshUser')).to.equal('321');
+    });
+  });
+
+  describe('#installType', function() {
+    it('should be manualDriven if manualInstall is selected', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {manualInstall: true}}});
+      expect(controller.get('installType')).to.equal('manualDriven');
+    });
+    it('should be ambariDriven if manualInstall isn\'t selected', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {manualInstall: false}}});
+      expect(controller.get('installType')).to.equal('ambariDriven');
+    });
+  });
+
   describe('#updateHostNameArr()', function () {
 
       var controller = App.WizardStep2Controller.create({
@@ -53,7 +111,7 @@ describe('App.WizardStep2Controller', function () {
       expect(controller.isAllHostNamesValid()).to.equal(true);
     });
 
-    var tests = [
+    var tests = Em.A([
       'hostname',
       '-hostname.com',
       'hostname-.com',
@@ -61,7 +119,7 @@ describe('App.WizardStep2Controller', function () {
       '123.123.123.123',
       'hostnamehostnamehostnamehostnamehostnamehostnamehostnamehostname.hostnamehostnamehostnamehostnamehostnamehostnamehostnamehostname.hostnamehostnamehostnamehostnamehostnamehostnamehostnamehostname.hostnamehostnamehostnamehostnamehostnamehostnamehostnamehostname',
       'hostnamehostnamehostnamehostnamehostnamehostnamehostnamehostnamehostname.hostname'
-    ];
+    ]);
     tests.forEach(function (test) {
       it('should return false for invalid host names ' + test + ' ', function () {
         controller.set('hostNames', test);
@@ -80,12 +138,6 @@ describe('App.WizardStep2Controller', function () {
       expect(controller.get('hostsError').length).to.be.above(2);
     });
 
-    /*it('should set hostsError if hostNames is invalid', function () {
-      controller.set('content', {'installOptions': {'hostNames': '@#$%'}});
-      controller.checkHostError();
-      expect(controller.get('hostsError').length).to.be.above(2);
-    })*/
-
     it('should set hostsError to null if hostNames is valid', function () {
       controller.set('content', {'installOptions': {'hostNames': 'ambari'}});
       controller.checkHostError();
@@ -117,20 +169,107 @@ describe('App.WizardStep2Controller', function () {
 
   describe('#sshKeyError', function () {
 
-    var controller = App.WizardStep2Controller.create({
-      manualInstall: false,
-      sshKey: '',
-      hasSubmitted: true
+    var tests = Em.A([
+      {
+        manualInstall: false,
+        sshKey: '',
+        hasSubmitted: false,
+        e: null
+      },
+      {
+        manualInstall: true,
+        sshKey: '',
+        hasSubmitted: false,
+        e: null
+      },
+      {
+        manualInstall: true,
+        sshKey: 'nobody',
+        hasSubmitted: false,
+        e: null
+      },
+      {
+        manualInstall: false,
+        sshKey: 'nobody',
+        hasSubmitted: false,
+        e: null
+      },
+      {
+        manualInstall: false,
+        sshKey: '',
+        hasSubmitted: true,
+        e: null
+      },
+      {
+        manualInstall: true,
+        sshKey: '',
+        hasSubmitted: true,
+        e: null
+      },
+      {
+        manualInstall: true,
+        sshKey: 'nobody',
+        hasSubmitted: true,
+        e: null
+      },
+      {
+        manualInstall: false,
+        sshKey: 'nobody',
+        hasSubmitted: true,
+        e: null
+      }
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.sshKey + ' ' + test.manualInstall.toString() + ' ' + test.hasSubmitted.toString(), function() {
+        var controller = App.WizardStep2Controller.create({content: {installOptions: {manualInstall: test.manualInstall, sshKey: test.sshKey}}});
+        if(Em.isNone(test.e)) {
+          expect(controller.get('sshKeyError')).to.equal(null);
+        }
+        else {
+          expect(controller.get('sshKeyError').length).to.be.above(2);
+        }
+      });
     });
+  });
+
+  describe('#sshUserError', function () {
+
+    var tests = Em.A([
+      {
+        manualInstall: false,
+        sshUser: '',
+        e: ''
+      },
+      {
+        manualInstall: true,
+        sshUser: '',
+        e: null
+      },
+      {
+        manualInstall: true,
+        sshUser: 'nobody',
+        e: null
+      },
+      {
+        manualInstall: false,
+        sshUser: 'nobody',
+        e: null
+      }
+    ]);
 
-    it('should return error message if hasSubmitted is true, manualInstall is false and sshKey is ""', function () {
-      expect(controller.get('sshKeyError').length).to.be.above(2);
+    tests.forEach(function(test) {
+      it('', function() {
+        var controller = App.WizardStep2Controller.create({content: {installOptions: {manualInstall: test.manualInstall, sshUser: test.sshUser}}});
+        if(Em.isNone(test.e)) {
+          expect(controller.get('sshUserError')).to.equal(null);
+        }
+        else {
+          expect(controller.get('sshUserError').length).to.be.above(2);
+        }
+      });
     });
 
-    it('should return null if hasSubmitted is false', function () {
-      controller.set('hasSubmitted', false);
-      expect(controller.get('sshKeyError')).to.equal(null);
-    })
   });
 
   describe('#getHostInfo()', function () {
@@ -202,13 +341,13 @@ describe('App.WizardStep2Controller', function () {
     })
   });
 
-  describe('#patternExpression()', function () {
+  describe('#parseHostNamesAsPatternExpression()', function () {
 
     it('should parse hosts from pattern expression to hostNameArr', function () {
       var controller = App.WizardStep2Controller.create({
         hostNameArr: ['host[001-011]']
       });
-      controller.patternExpression();
+      controller.parseHostNamesAsPatternExpression();
       var result = true;
       var hosts = controller.get('hostNameArr');
       for (var i = 1; i<12; i++) {
@@ -254,19 +393,110 @@ describe('App.WizardStep2Controller', function () {
     })
   });
 
-  /*describe('#saveHosts()', function () {
-    var controller = App.WizardStep2Controller.create({
-      hostNameArr: ['ambari']
+  describe('#installedHostsPopup', function() {
+    before(function() {
+      sinon.spy(App.ModalPopup, 'show');
+    });
+    after(function() {
+      App.ModalPopup.show.restore();
+    });
+    it('should call App.ModalPopup.show', function() {
+      var controller = App.WizardStep2Controller.create();
+      controller.installedHostsPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
     });
-    controller.set('content', Ember.Object.create({'hosts':Ember.Object.create({})}));
+  });
 
-    App.router = Ember.Object.create({
-      send:function() {}
+  describe('#warningPopup', function() {
+    before(function() {
+      sinon.spy(App.ModalPopup, 'show');
+    });
+    after(function() {
+      App.ModalPopup.show.restore();
     });
+    it('should call App.ModalPopup.show', function() {
+      var controller = App.WizardStep2Controller.create();
+      controller.warningPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+    });
+  });
+
+  describe('#hostNamePatternPopup', function() {
+    before(function() {
+      sinon.spy(App.ModalPopup, 'show');
+    });
+    after(function() {
+      App.ModalPopup.show.restore();
+    });
+    it('should call App.ModalPopup.show', function() {
+      var controller = App.WizardStep2Controller.create();
+      controller.hostNamePatternPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+    });
+  });
+
+  describe('#manualInstallPopup', function() {
+    before(function() {
+      sinon.spy(App.ModalPopup, 'show');
+    });
+    after(function() {
+      App.ModalPopup.show.restore();
+    });
+    it('should call App.ModalPopup.show', function() {
+      var controller = App.WizardStep2Controller.create();
+      controller.manualInstallPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+    });
+  });
+
+  describe('#manualInstallWarningPopup', function() {
+    beforeEach(function() {
+      sinon.spy(App.ModalPopup, 'show');
+    });
+    afterEach(function() {
+      App.ModalPopup.show.restore();
+    });
+    it('should call App.ModalPopup.show if content.installOptions.useSsh is false', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {useSsh: false}}});
+      controller.manualInstallWarningPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+    });
+    it('shouldn\'t call App.ModalPopup.show if content.installOptions.useSsh is true', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {useSsh: true}}});
+      controller.manualInstallWarningPopup();
+      expect(App.ModalPopup.show.called).to.equal(false);
+    });
+  });
+
+  describe('#setAmbariJavaHome', function() {
+    beforeEach(function() {
+      sinon.spy($, 'ajax');
+    });
+    afterEach(function() {
+      $.ajax.restore();
+    });
+    it('should do ajax-request', function() {
+      var controller = App.WizardStep2Controller.create({onGetAmbariJavaHomeSuccess: Em.K, onGetAmbariJavaHomeError: Em.K});
+      controller.setAmbariJavaHome();
+      expect($.ajax.calledOnce).to.equal(true);
+    });
+  });
+
+  describe('#onGetAmbariJavaHomeSuccess', function() {
+    it('should set java.home value receiced from server', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {}}});
+      var test = {RootServiceComponents: {properties: {'java.home': '/root'}}};
+      controller.onGetAmbariJavaHomeSuccess(test);
+      expect(controller.content.installOptions.javaHome).to.equal('/root');
+    });
+  });
+
+  describe('#onGetAmbariJavaHomeError', function() {
+    it('should set default java.home value', function() {
+      var controller = App.WizardStep2Controller.create({content: {installOptions: {}}});
+      controller.onGetAmbariJavaHomeError();
+      expect(controller.content.installOptions.javaHome).to.equal(App.get('defaultJavaHome'));
+    });
+  });
 
-    it('should set content.hosts', function () {
-      controller.saveHosts();
-      expect(controller.get('content.hosts')).to.not.be.empty;
-    })
-  })*/
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/test/views/wizard/step0_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/wizard/step0_view_test.js b/ambari-web/test/views/wizard/step0_view_test.js
new file mode 100644
index 0000000..22acdfa
--- /dev/null
+++ b/ambari-web/test/views/wizard/step0_view_test.js
@@ -0,0 +1,43 @@
+/**
+ * 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/step0_view');
+
+var view, controller = Em.Object.create({
+  clusterNameError: ''
+});
+
+describe('App.WizardStep0View', function () {
+
+  beforeEach(function() {
+    view = App.WizardStep0View.create({'controller': controller});
+  });
+
+  describe('#onError', function() {
+    it('should be true if clusterNameError appears', function() {
+      controller.set('clusterNameError', 'ERROR');
+      expect(view.get('onError')).to.equal(true);
+    });
+    it('should be false if clusterNameError doesn\'t appears', function() {
+      controller.set('clusterNameError', '');
+      expect(view.get('onError')).to.equal(false);
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/de1a9841/ambari-web/test/views/wizard/step1_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/wizard/step1_view_test.js b/ambari-web/test/views/wizard/step1_view_test.js
index 9d4673b..67b9ea3 100644
--- a/ambari-web/test/views/wizard/step1_view_test.js
+++ b/ambari-web/test/views/wizard/step1_view_test.js
@@ -19,177 +19,285 @@
 var App = require('app');
 require('views/wizard/step1_view');
 
+var view;
+
+var controller = Em.Object.create({
+  content: {
+    stacks: []
+  }
+});
+
 describe('App.WizardStep1View', function () {
-  var view = App.WizardStep1View.create({
-    stacks: [],
-    updateByCheckbox: function () {
-    },
-    editGroupLocalRepository: function () {
-    },
-    controller: Em.Object.create(),
-    allRepoUnchecked: false
+
+  beforeEach(function() {
+    view = App.WizardStep1View.create({'controller': controller});
   });
 
-  describe('#emptyRepoExist', function () {
-    it('none repos', function () {
-      view.set('allRepositoriesGroup', []);
-      expect(view.get('emptyRepoExist')).to.equal(false);
-    });
-    it('one not empty repo', function () {
-      view.set('allRepositoriesGroup', [
-        {
-          'empty-error': false
-        }
-      ]);
-      expect(view.get('emptyRepoExist')).to.equal(false);
+  describe('#osTypeToGroup', function() {
+
+    var tests = Em.A([
+      {os:'redhat5', e: 0},
+      {os: 'centos5', e: 0},
+      {os: 'oraclelinux5', e: 0},
+      {os: 'redhat6', e: 1},
+      {os: 'centos6', e: 1},
+      {os: 'oraclelinux6', e: 1},
+      {os: 'sles11', e: 2},
+      {os: 'suse11', e: 2},
+      {os: 'ubuntu12', e: 3},
+      {os: 'bulgen', e: -1}
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.os, function() {
+        expect(view.osTypeToGroup(test.os)).to.equal(test.e);
+      });
     });
-    it('one empty repo', function () {
-      view.set('allRepositoriesGroup', [
-        {
-          'empty-error': true
-        }
-      ]);
-      expect(view.get('emptyRepoExist')).to.equal(true);
+
+  });
+
+  describe('#groupToOsType', function () {
+
+    var tests = Em.A([
+      {type: 0, e: ['redhat5', 'centos5', 'oraclelinux5']},
+      {type: 1, e: ['redhat6', 'centos6', 'oraclelinux6']},
+      {type: 2, e: ['sles11', 'suse11']},
+      {type: 3, e: ['ubuntu12']},
+      {type: -1, e: []}
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.type, function() {
+        expect(view.groupToOsType(test.type)).to.eql(test.e);
+      });
     });
+
   });
 
-  describe('#invalidUrlExist', function () {
-    var invalidUrlExistTestCases = [
+  describe('#emptyRepoExist', function() {
+
+    var tests = Em.A([
       {
-        title: 'if invalid count more than 0 and validation failed then invalid URL should exist',
-        stacks: [
-          Em.Object.create({
-            isSelected: true,
-            invalidCnt: 1
-          })
-        ],
-        allRepositoriesGroup: [
-          {
-            'validation': 'icon-exclamation-sign'
-          }
-        ],
-        result: true
+        allRepositoriesGroup: [{'empty-error': false},{'empty-error': false},{'empty-error': false}],
+        e: false
       },
       {
-        title: 'if invalid count equal 0 and validation failed then invalid URL shouldn\'t exist',
-        stacks: [
-          Em.Object.create({
-            isSelected: true,
-            invalidCnt: 0
-          })
-        ],
-        allRepositoriesGroup: [
-          {
-            'validation': 'icon-exclamation-sign'
-          }
-        ],
-        result: false
+        allRepositoriesGroup: [{'empty-error': true},{'empty-error': false},{'empty-error': false}],
+        e: true
       },
       {
-        title: 'if invalid count more than 0 and validation passed then invalid URL shouldn\'t exist',
-        stacks: [
-          Em.Object.create({
-            isSelected: true,
-            invalidCnt: 1
-          })
-        ],
-        allRepositoriesGroup: [
-          {
-            'validation': 'icon-success'
-          }
-        ],
-        result: false
+        allRepositoriesGroup: [{'empty-error': true},{'empty-error': true},{'empty-error': true}],
+        e: true
+      }
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.allRepositoriesGroup.mapProperty('empty-error'), function() {
+        view.set('allRepositoriesGroup', test.allRepositoriesGroup);
+        expect(view.get('emptyRepoExist')).to.equal(test.e);
+      });
+    });
+
+  });
+
+  describe('#allRepoUnchecked', function() {
+
+    var tests = Em.A([
+      {
+        allRepositoriesGroup: [{'checked': false},{'checked': false},{'checked': false}],
+        e: true
       },
       {
-        title: 'if invalid count equal 0 and validation passed then invalid URL shouldn\'t exist',
-        stacks: [
-          Em.Object.create({
-            isSelected: true,
-            invalidCnt: 0
-          })
-        ],
-        allRepositoriesGroup: [
-          {
-            'validation': 'icon-success'
-          }
-        ],
-        result: false
+        allRepositoriesGroup: [{'checked': true},{'checked': false},{'checked': false}],
+        e: false
+      },
+      {
+        allRepositoriesGroup: [{'checked': true},{'checked': true},{'checked': true}],
+        e: false
       }
-    ];
+    ]);
 
-    invalidUrlExistTestCases.forEach(function (test) {
-      it(test.title, function () {
-        view.get('controller').set('content', {stacks: test.stacks});
+    tests.forEach(function(test) {
+      it(test.allRepositoriesGroup.mapProperty('empty-error'), function() {
         view.set('allRepositoriesGroup', test.allRepositoriesGroup);
-        expect(view.get('invalidUrlExist')).to.equal(test.result);
+        expect(view.get('allRepoUnchecked')).to.equal(test.e);
       });
     });
+
   });
-  describe('#totalErrorCnt', function () {
-    var totalErrorCntTestCases = [
+
+  describe('#stacks', function() {
+
+    var tests = Em.A([
       {
-        title: 'if allRepoUnchecked is true then totalErrorCnt should be 1',
-        allRepoUnchecked: true,
-        allRepositoriesGroup: [
-          {
-            'empty-error': true,
-            'validation': 'icon-exclamation-sign'
-          }
+        m: 'Stack with 2 HDP',
+        stacks: [
+          Em.Object.create({isSelected: true, name: 'HDP-2.0.1'}),
+          Em.Object.create({isSelected: false, name: 'HDP-1.3.3'})
         ],
-        result: 1
+        e: {
+          names: ['HDP 2.0.1', 'HDP 1.3.3'],
+          selected: [true, false]
+        }
       },
       {
-        title: 'if validation passed successfully then totalErrorCnt should be 0',
+        m: 'No HDP',
+        stacks: [],
+        e: {
+          names: [],
+          selected: []
+        }
+      }
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        view.set('controller.content.stacks', test.stacks);
+        var stacks = view.get('stacks');
+        expect(stacks.mapProperty('name')).to.eql(test.e.names);
+        expect(stacks.mapProperty('isSelected')).to.eql(test.e.selected);
+      });
+    });
+
+  });
+
+  describe('#isSubmitDisabled', function() {
+
+    var tests = Em.A([
+      {
+        emptyRepoExist: false,
         allRepoUnchecked: false,
-        allRepositoriesGroup: [
-          {
-            'empty-error': false,
-            'validation': 'icon-success'
-          }
-        ],
-        result: 0
+        invalidUrlExist: false,
+        e: false
       },
       {
-        title: 'if empty-error is true then totalErrorCnt should be 1',
+        emptyRepoExist: true,
         allRepoUnchecked: false,
-        allRepositoriesGroup: [
-          {
-            'empty-error': true,
-            'validation': 'icon-success'
-          }
-        ],
-        result: 1
+        invalidUrlExist: false,
+        e: true
+      },
+      {
+        emptyRepoExist: false,
+        allRepoUnchecked: true,
+        invalidUrlExist: false,
+        e: true
       },
       {
-        title: 'if validation failed then totalErrorCnt should be 1',
+        emptyRepoExist: false,
         allRepoUnchecked: false,
-        allRepositoriesGroup: [
-          {
-            'empty-error': false,
-            'validation': 'icon-exclamation-sign'
-          }
-        ],
-        result: 1
+        invalidUrlExist: true,
+        e: true
       },
       {
-        title: 'if validation failed and empty-error is true then totalErrorCnt should be 2',
+        emptyRepoExist: true,
         allRepoUnchecked: false,
-        allRepositoriesGroup: [
-          {
-            'empty-error': true,
-            'validation': 'icon-exclamation-sign'
-          }
-        ],
-        result: 2
+        invalidUrlExist: true,
+        e: true
+      },
+      {
+        emptyRepoExist: true,
+        allRepoUnchecked: true,
+        invalidUrlExist: false,
+        e: true
+      },
+      {
+        emptyRepoExist: false,
+        allRepoUnchecked: true,
+        invalidUrlExist: true,
+        e: true
+      },
+      {
+        emptyRepoExist: true,
+        allRepoUnchecked: true,
+        invalidUrlExist: true,
+        e: true
       }
-    ];
+    ]);
+
+    tests.forEach(function(test) {
+      it(test.emptyRepoExist.toString() + ' ' + test.allRepoUnchecked.toString() + ' ' + test.invalidUrlExist.toString(), function() {
+        view = App.WizardStep1View.create();
+        view.reopen({
+          emptyRepoExist: test.emptyRepoExist,
+          allRepoUnchecked: test.allRepoUnchecked,
+          invalidUrlExist: test.invalidUrlExist
+        });
+        expect(view.get('isSubmitDisabled')).to.equal(test.e);
+      });
+    });
 
-    totalErrorCntTestCases.forEach(function (test) {
-      it(test.title, function () {
-        view.set('allRepoUnchecked', test.allRepoUnchecked);
+  });
+
+  describe('#invalidUrlExist', function() {
+    var tests = Em.A([
+      {
+        stacks: [Em.Object.create({isSelected: true, invalidCnt: 1})],
+        allRepositoriesGroup: [Em.Object.create({validation: 'icon-exclamation-sign'})],
+        m: 'invalidCnt: 1, validation: icon-exclamation-sign',
+        e: true
+      },
+      {
+        stacks: [Em.Object.create({isSelected: true, invalidCnt: 1})],
+        allRepositoriesGroup: [Em.Object.create({validation: ''})],
+        m: 'invalidCnt: 1, validation: ""',
+        e: false
+      },
+      {
+        stacks: [Em.Object.create({isSelected: true, invalidCnt: 0})],
+        allRepositoriesGroup: [Em.Object.create({validation: ''})],
+        m: 'invalidCnt: 0, validation: ""',
+        e: false
+      },
+      {
+        stacks: [Em.Object.create({isSelected: true, invalidCnt: 0})],
+        allRepositoriesGroup: [Em.Object.create({validation: 'icon-exclamation-sign'})],
+        m: 'invalidCnt: 0, validation: icon-exclamation-sign',
+        e: false
+      }
+    ]);
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        view.set('controller.content.stacks', test.stacks);
         view.set('allRepositoriesGroup', test.allRepositoriesGroup);
-        expect(view.get('totalErrorCnt')).to.equal(test.result);
+        expect(view.get('invalidUrlExist')).to.equal(test.e);
       });
     });
   });
+
+  describe('#totalErrorCnt', function() {
+    var tests = Em.A([
+      {
+        allRepositoriesGroup: [{checked:false}],
+        m: 'allRepoUnchecked',
+        e: 1
+      },
+      {
+        allRepositoriesGroup: [{checked:true, 'empty-error': true}, {checked:false, 'empty-error': true}],
+        m: 'two with empty-error',
+        e: 2
+      },
+      {
+        allRepositoriesGroup: [{checked:true, 'validation': 'icon-exclamation-sign'}, {checked:false, 'validation': 'icon-exclamation-sign'}],
+        m: 'two with validation="icon-exclamation-sign"',
+        e: 2
+      },
+      {
+        allRepositoriesGroup: [{checked:true, 'empty-error': true, 'validation': 'icon-exclamation-sign'}, {checked:false, 'empty-error': true, 'validation': 'icon-exclamation-sign'}],
+        m: 'two with empty-error, two with validation="icon-exclamation-sign"',
+        e: 4
+      },
+      {
+        allRepositoriesGroup: [{checked:true}],
+        m: 'no errors/warnings etc',
+        e: 0
+      }
+    ]);
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        view.set('allRepositoriesGroup', test.allRepositoriesGroup);
+        expect(view.get('totalErrorCnt')).to.equal(test.e);
+      });
+    });
+  });
+
 });