You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2013/03/09 03:03:52 UTC

svn commit: r1454639 [1/2] - in /incubator/ambari/trunk: ./ ambari-web/ ambari-web/app/ ambari-web/app/assets/data/background_operations/ ambari-web/app/controllers/global/ ambari-web/app/controllers/wizard/ ambari-web/app/utils/ ambari-web/app/views/c...

Author: yusaku
Date: Sat Mar  9 02:03:51 2013
New Revision: 1454639

URL: http://svn.apache.org/r1454639
Log:
AMBARI-1583. Add unit tests for various Ambari Web components. (yusaku)

Added:
    incubator/ambari/trunk/ambari-web/app/assets/data/background_operations/one_task.json
    incubator/ambari/trunk/ambari-web/test/controllers/
    incubator/ambari/trunk/ambari-web/test/controllers/global/
    incubator/ambari/trunk/ambari-web/test/controllers/global/background_operations_test.js
    incubator/ambari/trunk/ambari-web/test/controllers/main/
    incubator/ambari/trunk/ambari-web/test/controllers/main/app_contoller_test.js
    incubator/ambari/trunk/ambari-web/test/controllers/main/charts/
    incubator/ambari/trunk/ambari-web/test/controllers/main/charts/heatmap_metrics/
    incubator/ambari/trunk/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js
    incubator/ambari/trunk/ambari-web/test/main/app/
    incubator/ambari/trunk/ambari-web/test/main/app/app_contoller_test.js
    incubator/ambari/trunk/ambari-web/test/utils/ajax_test.js
    incubator/ambari/trunk/ambari-web/test/utils/misc_test.js
    incubator/ambari/trunk/ambari-web/test/views/
    incubator/ambari/trunk/ambari-web/test/views/common/
    incubator/ambari/trunk/ambari-web/test/views/common/chart/
    incubator/ambari/trunk/ambari-web/test/views/common/chart/linear_time_test.js
Removed:
    incubator/ambari/trunk/ambari-web/vendor/scripts/sinon-1.4.2.js
Modified:
    incubator/ambari/trunk/CHANGES.txt
    incubator/ambari/trunk/ambari-web/app/controllers/global/background_operations_controller.js
    incubator/ambari/trunk/ambari-web/app/controllers/wizard/step1_controller.js
    incubator/ambari/trunk/ambari-web/app/controllers/wizard/step2_controller.js
    incubator/ambari/trunk/ambari-web/app/controllers/wizard/step4_controller.js
    incubator/ambari/trunk/ambari-web/app/initialize.js
    incubator/ambari/trunk/ambari-web/app/utils/ajax.js
    incubator/ambari/trunk/ambari-web/app/utils/updater.js
    incubator/ambari/trunk/ambari-web/app/views/common/chart/linear_time.js
    incubator/ambari/trunk/ambari-web/app/views/common/modal_popup.js
    incubator/ambari/trunk/ambari-web/app/views/wizard/step1_view.js
    incubator/ambari/trunk/ambari-web/app/views/wizard/step2_view.js
    incubator/ambari/trunk/ambari-web/app/views/wizard/step3_view.js
    incubator/ambari/trunk/ambari-web/config.coffee
    incubator/ambari/trunk/ambari-web/package.json
    incubator/ambari/trunk/ambari-web/test/installer/step1_test.js
    incubator/ambari/trunk/ambari-web/test/installer/step2_test.js
    incubator/ambari/trunk/ambari-web/test/installer/step4_test.js
    incubator/ambari/trunk/ambari-web/test/test-helpers.coffee
    incubator/ambari/trunk/ambari-web/test/utils/validator_test.js
    incubator/ambari/trunk/ambari-web/vendor/scripts/jquery.ui.custom-effects.js

Modified: incubator/ambari/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/CHANGES.txt?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/CHANGES.txt (original)
+++ incubator/ambari/trunk/CHANGES.txt Sat Mar  9 02:03:51 2013
@@ -111,6 +111,8 @@ Trunk (unreleased changes):
 
  IMPROVEMENTS
 
+ AMBARI-1583. Add unit tests for various Ambari Web components. (yusaku)
+
  AMBARI-1491. Add task plots to job swimlane diagram. (billie via yusaku)
 
  AMBARI-1584. Stack Upgrade Wizard - integrate host progress popup.

Added: incubator/ambari/trunk/ambari-web/app/assets/data/background_operations/one_task.json
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/assets/data/background_operations/one_task.json?rev=1454639&view=auto
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/assets/data/background_operations/one_task.json (added)
+++ incubator/ambari/trunk/ambari-web/app/assets/data/background_operations/one_task.json Sat Mar  9 02:03:51 2013
@@ -0,0 +1,18 @@
+{
+  "href" : "http://dev.hortonworks.com:8080/api/clusters/mycluster/requests/3/tasks/16",
+  "Tasks" : {
+    "exit_code" : 0,
+    "stdout" : "Output",
+    "status" : "QUEUED",
+    "stderr" : "none",
+    "host_name" : "dev.hortonworks.com",
+    "id" : 16,
+    "cluster_name" : "mycluster",
+    "attempt_cnt" : 1,
+    "request_id" : 3,
+    "command" : "STOP",
+    "role" : "NAMENODE",
+    "start_time" : 1352125378300,
+    "stage_id" : 1
+  }
+}

Modified: incubator/ambari/trunk/ambari-web/app/controllers/global/background_operations_controller.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/controllers/global/background_operations_controller.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/controllers/global/background_operations_controller.js (original)
+++ incubator/ambari/trunk/ambari-web/app/controllers/global/background_operations_controller.js Sat Mar  9 02:03:51 2013
@@ -30,89 +30,57 @@ App.BackgroundOperationsController = Em.
   allOperationsCount : 0,
   executeTasks: [],
 
-  getTasksByRole: function (role) {
-    return this.get('allOperations').filterProperty('role', role);
-  },
+  /**
+   * Task life time after finishing
+   */
+  taskLifeTime: 5*60*1000,
 
   getOperationsForRequestId: function(requestId){
     return this.get('allOperations').filterProperty('request_id', requestId);
   },
 
-  updateInterval: App.bgOperationsUpdateInterval,
-  url : '',
-
-  timeoutId : null,
-
-  /**
-   * Background operations will not be working if receive <code>attemptsCount</code> response with errors
-   */
-  attemptsCount: 20,
-
-  errorsCount: 0,
-
   /**
-   * Call this.loadOperations with delay
-   * @param delay time in milliseconds (updateInterval by default)
-   * @param reason reason why we call it(used to calculate count of errors)
+   * Start polling, when <code>isWorking</code> become true
    */
-  loadOperationsDelayed: function(delay, reason){
-    delay = delay || this.get('updateInterval');
-    var self = this;
-
-    if(reason && reason.indexOf('error:clusterName:') === 0){
-      var errors = this.get('errorsCount') + 1;
-      this.set('errorsCount', errors);
-      if(errors > this.get('attemptsCount')){
-        console.log('Stop loading background operations: clusterName is undefined');
-        return;
-      }
+  startPolling: function(){
+    if(this.get('isWorking')){
+      App.updater.run(this, 'loadOperations', 'isWorking', App.bgOperationsUpdateInterval);
     }
-
-    this.set('timeoutId',
-      setTimeout(function(){
-        self.loadOperations();
-      }, delay)
-    );
-  },
+  }.observes('isWorking'),
 
   /**
    * Reload operations
-   * We can call it manually <code>controller.loadOperations();</code>
-   * or it fires automatically, when <code>isWorking</code> becomes <code>true</code>
+   * @param callback on done Callback. Look art <code>App.updater.run</code> for more information
+   * @return jquery ajax object
    */
-  loadOperations : function(){
-
-    var timeoutId = this.get('timeoutId');
-    if(timeoutId){
-      clearTimeout(timeoutId);
-      this.set('timeoutId', null);
-    }
-
-    if(!this.get('isWorking')){
-      return;
-    }
+  loadOperations : function(callback){
 
-    if(!App.router.getClusterName()){
-      this.loadOperationsDelayed(this.get('updateInterval')/2, 'error:clusterName');
-      return;
+    if(!App.get('clusterName')){
+      callback();
+      return null;
     }
 
-    App.ajax.send({
+    return App.ajax.send({
       'name': 'background_operations',
       'sender': this,
-      'success': 'ajaxSuccess', //todo provide interfaces for strings and functions
-      'error': 'ajaxError'
-    })
-
-
-  }.observes('isWorking'),
-
-  ajaxSuccess: function(data) {
-    this.updateBackgroundOperations(data);
-    this.loadOperationsDelayed();
+      'success': 'updateBackgroundOperations', //todo provide interfaces for strings and functions
+      'callback': callback
+    });
   },
-  ajaxError: function(request, ajaxOptions, error) {
-    this.loadOperationsDelayed(null, 'error:response error');
+
+  /**
+   * Callback for update finished task request.
+   * @param data Json answer
+   */
+  updateFinishedTask: function(data){
+    var executeTasks = this.get('executeTasks');
+    if (data) {
+      var _oldTask = executeTasks.findProperty('id', data.Tasks.id);
+      if(_oldTask){
+        data.Tasks.finishedTime = new Date().getTime();
+        $.extend(_oldTask, data.Tasks);
+      }
+    }
   },
 
   /**
@@ -126,62 +94,59 @@ App.BackgroundOperationsController = Em.
     var executeTasks = this.get('executeTasks');
     data.items.forEach(function (item) {
       item.tasks.forEach(function (task) {
+        task.Tasks.display_exit_code = (task.Tasks.exit_code !== 999);
+
         if (task.Tasks.command == 'EXECUTE') {
-          if (!executeTasks.someProperty('id', task.Tasks.id)) {
+
+          var _oldTask = executeTasks.findProperty('id', task.Tasks.id);
+          if (!_oldTask) {
             executeTasks.push(task.Tasks);
+          } else {
+            $.extend(_oldTask, task.Tasks);
           }
-        } else {
-          if (task.Tasks.status == 'QUEUED' || task.Tasks.status == 'PENDING' || task.Tasks.status == 'IN_PROGRESS') {
-            runningTasks.push(task.Tasks);
-          }
+
+        } else if(['QUEUED', 'PENDING', 'IN_PROGRESS'].contains(task.Tasks.status)){
+          runningTasks.push(task.Tasks);
         }
       });
     });
 
-    for (var i = 0; i < executeTasks.length; i++) {
-      if (executeTasks[i].status == 'QUEUED' || executeTasks[i].status == 'PENDING' || executeTasks[i].status == 'IN_PROGRESS') {
-        var url = App.testMode ? '/data/background_operations/list_on_start.json' :
-            App.apiPrefix + '/clusters/' + App.router.getClusterName() + '/requests/' + executeTasks[i].request_id + '/tasks/' + executeTasks[i].id;
-        $.ajax({
-          type: "GET",
-          url: url,
-          dataType: 'json',
-          timeout: App.timeout,
-          success: function (data) {
-            if (data) {
-              for(var i = 0;i < executeTasks.length; i++){
-                if(data.Tasks.id == executeTasks[i].id){
-                  executeTasks[i] = data.Tasks;
-                }
-              }
-            }
-          },
-          error: function () {
-            console.log('ERROR: error during executeTask update');
-          },
+    var time = new Date().getTime() - this.get('taskLifeTime');
+    var tasksToRemove = [];
+    executeTasks.forEach(function(_task, index){
+      if(['FAILED', 'COMPLETED', 'TIMEDOUT', 'ABORTED'].contains(_task.status) && _task.finishedTime && _task.finishedTime < time){
+        tasksToRemove.push(index);
+      }
 
-          statusCode: require('data/statusCodes')
+      if(['QUEUED', 'PENDING', 'IN_PROGRESS'].contains(_task.status)){
+        App.ajax.send({
+          name: 'background_operations.update_task',
+          data: {
+            requestId: _task.request_id,
+            taskId: _task.id
+          },
+          'sender': this,
+          'success': 'updateFinishedTask'
         });
       }
-    }
+    }, this);
+
+
+    tasksToRemove.reverse().forEach(function(index){
+      executeTasks.removeAt(index);
+    });
+
+
     var currentTasks;
     currentTasks = runningTasks.concat(executeTasks);
     currentTasks = currentTasks.sort(function (a, b) {
       return a.id - b.id;
     });
 
-    // If the server is returning 999 as the return code, display blank and not 999
-    currentTasks.forEach( function (task) {
-      if (task.exit_code == 999) {
-        task.display_exit_code = false;
-      } else {
-        task.display_exit_code = true;
-      }
-    });
-
-    this.get('allOperations').filterProperty('isOpen').mapProperty('id').forEach(function(id){
-      if (currentTasks.someProperty('id', id)) {
-        currentTasks.findProperty('id', id).isOpen = true;
+    this.get('allOperations').filterProperty('isOpen').forEach(function(task){
+      var _task = currentTasks.findProperty('id', task.id);
+      if (_task) {
+        _task.isOpen = true;
       }
     });
 
@@ -210,17 +175,18 @@ App.BackgroundOperationsController = Em.
 
   /**
    * Onclick handler for background operations number located right to logo
+   * @return PopupObject For testing purposes
    */
   showPopup: function(){
     this.set('executeTasks', []);
-    this.loadOperations();
-    App.ModalPopup.show({
+    App.updater.immediateRun('loadOperations');
+    return App.ModalPopup.show({
       headerClass: Ember.View.extend({
-        controllerBinding: 'App.router.backgroundOperationsController',
+        controller: this,
         template:Ember.Handlebars.compile('{{allOperationsCount}} Background Operations Running')
       }),
       bodyClass: Ember.View.extend({
-        controllerBinding: 'App.router.backgroundOperationsController',
+        controller: this,
         templateName: require('templates/main/background_operations_popup')
       }),
       onPrimary: function() {
@@ -231,7 +197,7 @@ App.BackgroundOperationsController = Em.
   },
 
   /**
-   * Exaple of data inside:
+   * Example of data inside:
    * {
    *   when : function(backgroundOperationsController){
    *     return backgroundOperationsController.getOperationsForRequestId(requestId).length == 0;

Modified: incubator/ambari/trunk/ambari-web/app/controllers/wizard/step1_controller.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/controllers/wizard/step1_controller.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/controllers/wizard/step1_controller.js (original)
+++ incubator/ambari/trunk/ambari-web/app/controllers/wizard/step1_controller.js Sat Mar  9 02:03:51 2013
@@ -55,6 +55,7 @@ App.WizardStep1Controller = Em.Controlle
   /**
    * calculates by <code>invalidClusterName</code> property
    */
+  //todo: mix this and previous variables in one
   clusterNameError: '',
 
   /**

Modified: incubator/ambari/trunk/ambari-web/app/controllers/wizard/step2_controller.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/controllers/wizard/step2_controller.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/controllers/wizard/step2_controller.js (original)
+++ incubator/ambari/trunk/ambari-web/app/controllers/wizard/step2_controller.js Sat Mar  9 02:03:51 2013
@@ -57,7 +57,7 @@ App.WizardStep2Controller = Em.Controlle
     this.get('inputtedAgainHostNames').clear();
     var installedHostNames = App.Host.find().mapProperty('hostName');
     var tempArr = [];
-    for (i = 0; i < this.hostNameArr.length; i++) {
+    for (var i = 0; i < this.hostNameArr.length; i++) {
       if (!installedHostNames.contains(this.hostNameArr[i])) {
         tempArr.push(this.hostNameArr[i]);
       } else {
@@ -177,17 +177,7 @@ App.WizardStep2Controller = Em.Controlle
       return false;
     }
     if (this.get('inputtedAgainHostNames').length) {
-      var self = this;
-      App.ModalPopup.show({
-        header: Em.I18n.t('common.warning'),
-        onPrimary: function () {
-          self.proceedNext();
-          this.hide();
-        },
-        bodyClass: Ember.View.extend({
-          template: Ember.Handlebars.compile('<p>{{t installer.step2.evaluateStep.installedHosts}}</p><p>' + self.get('inputtedAgainHostNames').join(', ') + '</p><p>{{t installer.step2.evaluateStep.continueConfirm}}</p>')
-        })
-      });
+      this.installedHostsPopup();
     } else {
       this.proceedNext();
     }
@@ -256,6 +246,24 @@ App.WizardStep2Controller = Em.Controlle
       this.saveHosts();
     }
   },
+
+  /**
+   * show popup with the list of hosts that are already part of the cluster
+   */
+  installedHostsPopup: function () {
+    var self = this;
+    App.ModalPopup.show({
+      header: Em.I18n.t('common.warning'),
+      onPrimary: function () {
+        self.proceedNext();
+        this.hide();
+      },
+      bodyClass: Ember.View.extend({
+        template: Ember.Handlebars.compile('<p>These hosts are already installed on the cluster and will be ignored:</p><p>' + self.get('inputtedAgainHostNames').join(', ') + '</p><p>Do you want to continue?</p>')
+      })
+    });
+  },
+
   /**
    * show popup with hosts generated by pattern
    * @param hostNames

Modified: incubator/ambari/trunk/ambari-web/app/controllers/wizard/step4_controller.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/controllers/wizard/step4_controller.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/controllers/wizard/step4_controller.js (original)
+++ incubator/ambari/trunk/ambari-web/app/controllers/wizard/step4_controller.js Sat Mar  9 02:03:51 2013
@@ -101,17 +101,7 @@ App.WizardStep4Controller = Em.ArrayCont
    */
   validateMonitoring: function () {
     if (this.gangliaOrNagiosNotSelected()) {
-      App.ModalPopup.show({
-        header: Em.I18n.t('installer.step4.monitoringCheck.popup.header'),
-        body: Em.I18n.t('installer.step4.monitoringCheck.popup.body'),
-        onPrimary: function () {
-          this.hide();
-          App.router.send('next');
-        },
-        onSecondary: function () {
-          this.hide();
-        }
-      });
+      this.monitoringCheckPopup();
     } else {
       App.router.send('next');
     }
@@ -122,23 +112,41 @@ App.WizardStep4Controller = Em.ArrayCont
    */
   submit: function () {
     if(!this.get("isSubmitDisabled")){
-      var self = this;
       if (this.needToAddMapReduce()) {
-        App.ModalPopup.show({
-          header: Em.I18n.t('installer.step4.mapreduceCheck.popup.header'),
-          body: Em.I18n.t('installer.step4.mapreduceCheck.popup.body'),
-          onPrimary: function () {
-            self.findProperty('serviceName', 'MAPREDUCE').set('isSelected', true);
-            this.hide();
-            self.validateMonitoring();
-          },
-          onSecondary: function () {
-            this.hide();
-          }
-        });
+        this.mapReduceCheckPopup();
       } else {
-        self.validateMonitoring();
+        this.validateMonitoring();
       }
     }
+  },
+
+  mapReduceCheckPopup: function () {
+    var self = this;
+    App.ModalPopup.show({
+      header: Em.I18n.t('installer.step4.mapreduceCheck.popup.header'),
+      body: Em.I18n.t('installer.step4.mapreduceCheck.popup.body'),
+      onPrimary: function () {
+        self.findProperty('serviceName', 'MAPREDUCE').set('isSelected', true);
+        this.hide();
+        self.validateMonitoring();
+      },
+      onSecondary: function () {
+        this.hide();
+      }
+    });
+  },
+
+  monitoringCheckPopup: function () {
+    App.ModalPopup.show({
+      header: Em.I18n.t('installer.step4.monitoringCheck.popup.header'),
+      body: Em.I18n.t('installer.step4.monitoringCheck.popup.body'),
+      onPrimary: function () {
+        this.hide();
+        App.router.send('next');
+      },
+      onSecondary: function () {
+        this.hide();
+      }
+    });
   }
 })
\ No newline at end of file

Modified: incubator/ambari/trunk/ambari-web/app/initialize.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/initialize.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/initialize.js (original)
+++ incubator/ambari/trunk/ambari-web/app/initialize.js Sat Mar  9 02:03:51 2013
@@ -50,11 +50,6 @@ require('utils/host_progress_popup');
 
 App.initialize();
 
-/**
- * Test Mode values
- */
-App.test_hostname = 'hostname';
-
 console.log('after initialize');
 console.log('TRACE: app.js-> localStorage:Ambari.authenticated=' + localStorage.getItem('Ambari' + 'authenticated'));
 console.log('TRACE: app.js-> localStorage:currentStep=' + localStorage.getItem(App.get('router').getLoginName() + 'Installer' + 'currentStep'));

Modified: incubator/ambari/trunk/ambari-web/app/utils/ajax.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/utils/ajax.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/utils/ajax.js (original)
+++ incubator/ambari/trunk/ambari-web/app/utils/ajax.js Sat Mar  9 02:03:51 2013
@@ -35,6 +35,11 @@ var urls = {
     'real': '/clusters/{clusterName}/requests/?fields=tasks/*',
     'testInProduction': true
   },
+  'background_operations.update_task': {
+    'mock': '/data/background_operations/one_task.json',
+    'real': '/clusters/{clusterName}/requests/{requestId}/tasks/{taskId}',
+    'testInProduction': true
+  },
   'service.item.start_stop': {
     'mock': '/data/wizard/deploy/poll_1.json',
     'real': '/clusters/{clusterName}/services/{serviceName}',
@@ -112,7 +117,7 @@ App.ajax = {
    * Send ajax request
    *
    * @param {Object} config
-   * @return {Boolean}
+   * @return Object jquery ajax object
    *
    * config fields:
    *  name - url-key in the urls-object *required*
@@ -120,16 +125,17 @@ App.ajax = {
    *  data - object with data for url-format
    *  success - method-name for ajax success response callback
    *  error - method-name for ajax error response callback
+   *  callback - callback from <code>App.updater.run</code> library
    */
   send: function(config) {
     if (!config.sender) {
       console.warn('Ajax sender should be defined!');
-      return false;
+      return null;
     }
 
     // default parameters
     var params = {
-      clusterName: App.router.get('clusterController.clusterName')
+      clusterName: App.get('clusterName')
     };
 
     // extend default parameters with provided
@@ -151,6 +157,15 @@ App.ajax = {
         config.sender[config.error](request, ajaxOptions, error, opt);
       }
     };
-    $.ajax(opt);
+    opt.complete = function(){
+      if(config.callback){
+        config.callback();
+      }
+    };
+    if($.mocho){
+      opt.url = 'http://' + $.hostName + opt.url;
+    }
+
+    return $.ajax(opt);
   }
 }

Modified: incubator/ambari/trunk/ambari-web/app/utils/updater.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/utils/updater.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/utils/updater.js (original)
+++ incubator/ambari/trunk/ambari-web/app/utils/updater.js Sat Mar  9 02:03:51 2013
@@ -19,7 +19,7 @@ var App = require('app');
 
 var states = {};
 
-function update(obj, name, isWorking){
+function update(obj, name, isWorking, interval){
   if(typeof isWorking == 'string' && !obj.get(isWorking)){
     return false;
   }
@@ -28,12 +28,16 @@ function update(obj, name, isWorking){
 
   if(!state){
     var callback = function(){
-      update(obj, name, isWorking);
+      update(obj, name, isWorking, interval);
     };
     states[name] = state = {
       timeout: null,
       func: function(){
+        if(typeof isWorking == 'string' && !obj.get(isWorking)){
+          return false;
+        }
         obj[name](callback);
+        return true;
       },
       callback: callback
     };
@@ -41,7 +45,7 @@ function update(obj, name, isWorking){
 
   clearTimeout(state.timeout);
 
-  state.timeout = setTimeout(state.func, App.contentUpdateInterval);
+  state.timeout = setTimeout(state.func, interval);
   return true;
 };
 
@@ -99,10 +103,12 @@ App.updater = {
    * @param obj Object
    * @param name Method name
    * @param isWorking Property, which will be checked as a rule for working
+   * @param interval Interval between calls
    * @return {*}
    */
-  run: function(obj, name, isWorking){
-    return update(obj, name, isWorking);
+  run: function(obj, name, isWorking, interval){
+    interval = interval || App.contentUpdateInterval;
+    return update(obj, name, isWorking, interval);
   },
 
   /**

Modified: incubator/ambari/trunk/ambari-web/app/views/common/chart/linear_time.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/views/common/chart/linear_time.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/views/common/chart/linear_time.js (original)
+++ incubator/ambari/trunk/ambari-web/app/views/common/chart/linear_time.js Sat Mar  9 02:03:51 2013
@@ -251,6 +251,9 @@ App.ChartLinearTimeView = Ember.View.ext
    * @type Function
    */
   yAxisFormatter: function(y) {
+    if(isNaN(y)){
+      return 0;
+    }
     var value = Rickshaw.Fixtures.Number.formatKMBT(y);
     if (value == '') return '0';
     value = String(value);
@@ -292,7 +295,7 @@ App.ChartLinearTimeView = Ember.View.ext
     }
     var result = true;
     seriesData.forEach(function(item){
-      if(!item.data.length || !item.data[0] || typeof item.data[0].x === 'undefined'){
+      if(!item.data || !item.data.length || !item.data[0] || typeof item.data[0].x === 'undefined'){
         result = false;
       }
     });

Modified: incubator/ambari/trunk/ambari-web/app/views/common/modal_popup.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/views/common/modal_popup.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/views/common/modal_popup.js (original)
+++ incubator/ambari/trunk/ambari-web/app/views/common/modal_popup.js Sat Mar  9 02:03:51 2013
@@ -57,6 +57,7 @@ App.ModalPopup = Ember.View.extend({
   autoHeight: true,
 
   onPrimary: function() {
+    this.hide();
   },
 
   onSecondary: function() {
@@ -80,7 +81,6 @@ App.ModalPopup = Ember.View.extend({
 
   didInsertElement: function(){
     if(this.autoHeight){
-      this._super();
       var block = this.$().find('#modal > .modal-body').first();
       block.css('max-height', $(window).height() - block.offset().top - 300 + $(window).scrollTop()); // fix popup height
     }

Modified: incubator/ambari/trunk/ambari-web/app/views/wizard/step1_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/views/wizard/step1_view.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/views/wizard/step1_view.js (original)
+++ incubator/ambari/trunk/ambari-web/app/views/wizard/step1_view.js Sat Mar  9 02:03:51 2013
@@ -21,22 +21,19 @@ var App = require('app');
 
 App.WizardStep1View = Em.View.extend({
 
-  tagName: "form",
+  tagName: "form", //todo: why form?
   templateName: require('templates/wizard/step1'),
 
+  //todo: create property for placeholder(go to template)
+
   didInsertElement: function () {
     $("[rel=popover]").popover({'placement': 'right', 'trigger': 'hover'});
-    console.log("The value of cluster controller is: "+ this.get('controller.name'));
     this.get('controller').loadStep();
   },
 
+  //todo: rename it to class or write comments
   onError: function () {
     return this.get('controller.clusterNameError') !== '';
-  }.property('controller.clusterNameError'),
-
-  submit: function(event) {
-    this.get('controller').submit();
-    return false;
-  }
+  }.property('controller.clusterNameError')
 
 });

Modified: incubator/ambari/trunk/ambari-web/app/views/wizard/step2_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/views/wizard/step2_view.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/views/wizard/step2_view.js (original)
+++ incubator/ambari/trunk/ambari-web/app/views/wizard/step2_view.js Sat Mar  9 02:03:51 2013
@@ -20,6 +20,9 @@
 var App = require('app');
 
 App.SshKeyFileUploader = Ember.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"}} />'),
   classNames: ['ssh-key-input-indentation'],
 
@@ -40,6 +43,7 @@ App.SshKeyFileUploader = Ember.View.exte
   }
 });
 
+//TODO: move it to App.WizardStep2View
 App.WizardTextField = Ember.TextField.extend({
   disabled: function(){
     return !this.get('controller.content.installOptions.isJavaHome');
@@ -52,34 +56,21 @@ App.WizardTextField = Ember.TextField.ex
 App.WizardStep2View = Em.View.extend({
 
   templateName: require('templates/wizard/step2'),
-  hostNameErr: false,
-  hostsInfo: null,
 
   didInsertElement: function () {
+    //TODO: move it to separate function in Ember.View using reopenClass
     $("[rel=popover]").popover({'placement': 'right', 'trigger': 'hover'});
-    this.set('hostNameErr', false);
+
+    //todo: move them to conroller
     this.set('controller.hostsError',null);
     this.set('controller.sshKeyError',null);
-    this.loadHostsInfo();
-  },
-
-  loadHostsInfo: function(){
-    var hostsInfo = Em.Object.create();
-    this.set('hostsInfo', hostsInfo);
   },
 
-  onHostNameErr: function () {
-    if (this.get('controller.hostNameEmptyError') === false && this.get('controller.hostNameNotRequiredErr') === false && this.get('controller.hostNameErr') === false) {
-      this.set('hostNameErr', false);
-    } else {
-      this.set('hostNameErr', true);
-    }
-  }.observes('controller.hostNameEmptyError', 'controller.hostNameNotRequiredErr', 'controller.hostNameErr'),
-
   sshKeyState: function(){
     return this.get("controller.content.installOptions.manualInstall");
   }.property("controller.content.installOptions.manualInstall"),
 
+  //TODO: incupsulate it inside of App.SshKeyFileUploader
   isFileApi: function () {
     return (window.File && window.FileReader && window.FileList) ? true : false ;
   }.property(),
@@ -99,6 +90,7 @@ App.WizardStep2View = Em.View.extend({
     this.set('controller.content.installOptions.manualInstall', !this.get('controller.content.installOptions.useSsh'));
   }.observes('controller.content.installOptions.useSsh'),
 
+  //TODO: replace next 2 properties with new one used in both places
   providingSSHKeyRadioButton: Ember.Checkbox.extend({
     tagName: 'input',
     attributeBindings: ['type', 'checked'],

Modified: incubator/ambari/trunk/ambari-web/app/views/wizard/step3_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/app/views/wizard/step3_view.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/app/views/wizard/step3_view.js (original)
+++ incubator/ambari/trunk/ambari-web/app/views/wizard/step3_view.js Sat Mar  9 02:03:51 2013
@@ -29,6 +29,7 @@ App.WizardStep3View = Em.View.extend({
   }
 });
 
+//todo: move it inside WizardStep3View
 App.WizardHostView = Em.View.extend({
 
   tagName: 'tr',

Modified: incubator/ambari/trunk/ambari-web/config.coffee
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/config.coffee?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/config.coffee (original)
+++ incubator/ambari/trunk/ambari-web/config.coffee Sat Mar  9 02:03:51 2013
@@ -41,7 +41,6 @@ exports.config =
           'vendor/scripts/bootstrap.js',
           'vendor/scripts/bootstrap-combobox.js'
           'vendor/scripts/d3.v2.js',
-          'vendor/scripts/sinon-1.4.2.js',
           'vendor/scripts/cubism.v1.js',
           'vendor/scripts/jquery.ui.core.js',
           'vendor/scripts/jquery.ui.widget.js',
@@ -56,7 +55,8 @@ exports.config =
           'vendor/scripts/workflow_visualization.js',
           'vendor/scripts/rickshaw.js',
           'vendor/scripts/spin.js',
-          'vendor/scripts/jquery.flexibleArea.js'
+          'vendor/scripts/jquery.flexibleArea.js',
+          'test/utils/ajax_test.js'
           ]
 
     stylesheets:

Modified: incubator/ambari/trunk/ambari-web/package.json
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/package.json?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/package.json (original)
+++ incubator/ambari/trunk/ambari-web/package.json Sat Mar  9 02:03:51 2013
@@ -26,6 +26,7 @@
     "chai":"1.2.0",
     "sinon":"1.4.2",
     "sinon-chai":"2.1.2",
-    "express":"2.5.8"
+    "express":"2.5.8",
+    "xmlhttprequest":"1.5.0"
   }
 }

Added: incubator/ambari/trunk/ambari-web/test/controllers/global/background_operations_test.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/test/controllers/global/background_operations_test.js?rev=1454639&view=auto
==============================================================================
--- incubator/ambari/trunk/ambari-web/test/controllers/global/background_operations_test.js (added)
+++ incubator/ambari/trunk/ambari-web/test/controllers/global/background_operations_test.js Sat Mar  9 02:03:51 2013
@@ -0,0 +1,347 @@
+/**
+ * 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('config');
+require('utils/updater');
+require('utils/ajax');
+
+require('controllers/global/background_operations_controller');
+require('views/common/modal_popup');
+
+/*window.console.log = function(text){
+  console.log(text);
+}*/
+
+describe('App.BackgroundOperationsController', function () {
+
+  /**
+   * Predefined data
+   *
+   */
+  App.set('clusterName', 'testName');
+  App.set('testMode', 'true');
+  App.bgOperationsUpdateInterval = 100;
+
+  /**
+   * Test object
+   */
+  var controller = App.BackgroundOperationsController.create();
+
+  describe('#getOperationsForRequestId', function(){
+
+    var obj = new window.Array({"exit_code":0,"stdout":"Output","status":"COMPLETED","stderr":"none","host_name":"dev.hortonworks.com","id":10,"cluster_name":"mycluster","attempt_cnt":1,"request_id":2,"command":"EXECUTE","role":"HDFS_SERVICE_CHECK","start_time":1352119106480,"stage_id":2,"display_exit_code":true},{"exit_code":0,"stdout":"Output","status":"COMPLETED","stderr":"none","host_name":"dev.hortonworks.com","id":14,"cluster_name":"mycluster","attempt_cnt":1,"request_id":2,"command":"EXECUTE","role":"MAPREDUCE_SERVICE_CHECK","start_time":1352119157294,"stage_id":3,"display_exit_code":true},{"exit_code":0,"stdout":"Output","status":"QUEUED","stderr":"none","host_name":"dev.hortonworks.com","id":16,"cluster_name":"mycluster","attempt_cnt":1,"request_id":3,"command":"STOP","role":"NAMENODE","start_time":1352125378300,"stage_id":1,"display_exit_code":true});
+    var controller = App.BackgroundOperationsController.create();
+
+
+    it('test 1 with 3 items  ', function(){
+
+      controller.set('allOperations', obj);
+      expect(controller.getOperationsForRequestId(1).length).to.be.equal(0);
+      expect(controller.getOperationsForRequestId(2).length).to.be.equal(2);
+      expect(controller.getOperationsForRequestId(3).length).to.be.equal(1);
+
+    });
+
+
+
+    it('test 2 with 0 items  ', function(){
+
+      controller.set('allOperations', new window.Array());
+      expect(controller.getOperationsForRequestId(1).length).to.be.equal(0);
+      expect(controller.getOperationsForRequestId(2).length).to.be.equal(0);
+      expect(controller.getOperationsForRequestId(3).length).to.be.equal(0);
+
+    });
+
+    it('test 3 with 9 items  ', function(){
+
+      controller.set('allOperations', obj.concat(obj, obj));
+      expect(controller.getOperationsForRequestId(1).length).to.be.equal(0);
+      expect(controller.getOperationsForRequestId(2).length).to.be.equal(6);
+      expect(controller.getOperationsForRequestId(3).length).to.be.equal(3);
+
+    });
+
+  });
+
+  describe('when set isWorking to true  ', function () {
+
+    it('startPolling executes App.updater.run  ', function(done){
+      sinon.stub(App.updater, 'run', function(){
+        controller.set('isWorking', false);
+        App.updater.run.restore();
+        done();
+      });
+
+      controller.set('isWorking', true);
+    });
+
+    it('loadOperations should be called  ', function(done){
+      this.timeout(App.bgOperationsUpdateInterval + 500);
+
+      sinon.stub(controller, 'loadOperations', function(){
+        controller.set('isWorking', false);
+        controller.loadOperations.restore();
+        done();
+      });
+
+      controller.set('isWorking', true);
+    });
+
+    it('updateBackgroundOperations should be called  ', function(done){
+      this.timeout(App.bgOperationsUpdateInterval + 500);
+
+      sinon.stub(controller, 'updateBackgroundOperations', function(){
+        controller.set('isWorking', false);
+        controller.updateBackgroundOperations.restore();
+        done();
+      });
+
+      controller.set('isWorking', true);
+    });
+
+    it('allOperations should be set  ', function(done){
+      this.timeout(App.bgOperationsUpdateInterval + 500);
+
+      sinon.stub(controller, 'updateBackgroundOperations', function(data){
+        controller.set('isWorking', false);
+        controller.updateBackgroundOperations.restore();
+        controller.updateBackgroundOperations(data);
+        expect(controller.get('executeTasks').length).to.be.equal(2);
+        expect(controller.get('allOperationsCount')).to.be.equal(1);
+
+        var bgOperation = controller.get('allOperations')[0];
+        expect(bgOperation).to.have.property('id');
+        expect(bgOperation).to.have.property('request_id');
+        expect(bgOperation).to.have.property('role');
+        expect(bgOperation).to.have.property('command');
+        expect(bgOperation).to.have.property('status');
+
+        done();
+      });
+
+      controller.set('isWorking', true);
+    });
+
+  })
+
+  describe('#showPopup', function () {
+    it('works without exceptions  ', function(){
+      var popup = controller.showPopup();
+      popup.onPrimary();
+    });
+  });
+
+  function generateTask(time, role, status, id, command){
+    command = command || 'STOP';
+    return {
+      "Tasks" : {
+        "exit_code" : 0,
+        "stdout" : "Output",
+        "status" : status,
+        "stderr" : "none",
+        "host_name" : "dev.hortonworks.com",
+        "id" : id,
+        "cluster_name" : "mycluster",
+        "attempt_cnt" : 1,
+        "request_id" : 1,
+        "command" : command,
+        "role" : role,
+        "start_time" : time,
+        "stage_id" : 1
+      }
+    };
+  }
+
+  function generate_test_json(){
+
+    var time = new Date().getTime();
+
+    return {
+      items:[
+        {
+          "Requests" : {
+            "id" : 3,
+            "cluster_name" : "mycluster"
+          },
+          "tasks":[
+            generateTask(time, 'NAMENODE', 'QUEUED', 16),
+            generateTask(time, 'DATANODE', 'QUEUED', 15),
+            generateTask(time, 'SECONDARY_NAMENODE', 'QUEUED', 14),
+            generateTask(time, 'MAPREDUCE_SERVICE_CHECK', 'QUEUED', 13, 'EXECUTE')
+          ]
+        }
+      ]
+    };
+  }
+
+  function update_test_json(json, index, state){
+    var item = json.items[0].tasks[index].Tasks;
+    item.status = state;
+    item.finishedTime = new Date().getTime();
+  }
+
+  function add_to_test_json(json){
+    var tasks = json.items[0].tasks;
+    var item = tasks[0].Tasks;
+    tasks.push(generateTask(item.start_time, 'HBASE_MASTER', 'QUEUED', 12));
+    tasks.push(generateTask(item.start_time, 'ZOOKEEPER_SERVER', 'QUEUED', 11));
+  }
+
+  describe('#updateBackgroundOperations', function () {
+    describe('finished items are removed with delay  ', function(){
+      controller.set('allOperations', new window.Array());
+      controller.set('executeTasks', new window.Array());
+      controller.set('allOperationsCount', 0);
+
+      var json = generate_test_json();
+
+      it('on start we have 4 tasks  ', function(){
+        controller.updateBackgroundOperations(json);
+        expect(controller.get('allOperationsCount')).to.be.equal(4);
+        expect(controller.get('allOperations').length).to.be.equal(4);
+        expect(controller.get('executeTasks').length).to.be.equal(1);
+      })
+
+      it('first task is in progress  ', function(){
+        update_test_json(json, 2, 'IN_PROGRESS');
+        controller.updateBackgroundOperations(json);
+        expect(controller.get('allOperationsCount')).to.be.equal(4);
+        expect(controller.get('allOperations').length).to.be.equal(4);
+        expect(controller.get('executeTasks').length).to.be.equal(1);
+      });
+
+      it('first task finished  ', function(){
+        update_test_json(json, 2, 'COMPLETED');
+        controller.updateBackgroundOperations(json);
+        expect(controller.get('allOperationsCount')).to.be.equal(3);
+        expect(controller.get('allOperations').length).to.be.equal(3);
+        expect(controller.get('executeTasks').length).to.be.equal(1);
+      });
+
+      it('second task is in progress  ', function(){
+        update_test_json(json, 3, 'IN_PROGRESS');
+        controller.updateBackgroundOperations(json);
+        expect(controller.get('allOperationsCount')).to.be.equal(3);
+        expect(controller.get('allOperations').length).to.be.equal(3);
+        expect(controller.get('executeTasks').length).to.be.equal(1);
+      });
+
+      it('second task finished  ', function(){
+        update_test_json(json, 3, 'COMPLETED');
+        controller.updateBackgroundOperations(json);
+        expect(controller.get('allOperationsCount')).to.be.equal(2);
+        expect(controller.get('allOperations').length).to.be.equal(3);
+        expect(controller.get('executeTasks').length).to.be.equal(1);
+      });
+
+      var oldLifeTime = controller.get('taskLifeTime');
+      controller.set('taskLifeTime', 10);
+
+      it('second task removed from list  ', function(done){
+        setTimeout(function(){
+          controller.updateBackgroundOperations(json);
+          expect(controller.get('allOperationsCount')).to.be.equal(2);
+          expect(controller.get('allOperations').length).to.be.equal(2);
+          expect(controller.get('executeTasks').length).to.be.equal(0);
+
+          done();
+        }, controller.get('taskLifeTime'));
+      });
+
+      it('add new items  ', function(){
+        add_to_test_json(json);
+        controller.updateBackgroundOperations(json);
+        expect(controller.get('allOperationsCount')).to.be.equal(4);
+        expect(controller.get('allOperations').length).to.be.equal(4);
+        expect(controller.get('executeTasks').length).to.be.equal(0);
+        controller.set('taskLifeTime', oldLifeTime);
+      });
+    });
+  });
+
+  describe('#updateFinishedTask  ', function(){
+
+    var json = null;
+
+    beforeEach(function(){
+      controller.set('allOperations', new window.Array());
+      controller.set('executeTasks', new window.Array());
+      controller.set('allOperationsCount', 0);
+
+      json = generate_test_json();
+      controller.updateBackgroundOperations(json);
+    });
+
+    it('update task  ', function(){
+      update_test_json(json, 3, 'COMPLETED');
+      controller.updateFinishedTask(json.items[0].tasks[3]);
+
+      var item = controller.get('allOperations').findProperty('id', 13);
+      expect(item).to.be.an('object');
+      expect(item).to.have.property('status', 'COMPLETED');
+      expect(item.finishedTime).to.be.above(0);
+    })
+
+    it("don't update task  ", function(){
+      controller.updateFinishedTask({ Tasks: { id : 1 } });
+
+      var item = controller.get('allOperations').findProperty('id', 13);
+      expect(item).to.be.an('object');
+      expect(item).to.have.property('status', 'QUEUED');
+      expect(item.finishedTime).to.be.equal(undefined);
+    })
+
+  });
+
+  describe('#eventsArray  ', function(){
+
+    var json = null;
+
+    beforeEach(function(){
+      controller.set('allOperations', new window.Array());
+      controller.set('executeTasks', new window.Array());
+      controller.set('allOperationsCount', 0);
+
+      json = generate_test_json();
+      controller.updateBackgroundOperations(json);
+    });
+
+    it("it's working  ", function(done){
+      controller.get('eventsArray').push({
+        "when" : function(controller){
+          return controller.get('allOperationsCount') == 3;
+        },
+        "do" : done
+      });
+
+      controller.updateBackgroundOperations(json);
+      controller.updateBackgroundOperations(json);
+      controller.updateBackgroundOperations(json);
+      controller.updateBackgroundOperations(json);
+
+      update_test_json(json, 1, 'COMPLETED');
+      controller.updateBackgroundOperations(json);
+    })
+  });
+
+
+})

Added: incubator/ambari/trunk/ambari-web/test/controllers/main/app_contoller_test.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/test/controllers/main/app_contoller_test.js?rev=1454639&view=auto
==============================================================================
--- incubator/ambari/trunk/ambari-web/test/controllers/main/app_contoller_test.js (added)
+++ incubator/ambari/trunk/ambari-web/test/controllers/main/app_contoller_test.js Sat Mar  9 02:03:51 2013
@@ -0,0 +1,188 @@
+/**
+ * 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('controllers/main/apps_controller');
+/* require('models/cluster');
+ require('models/service');
+ require('models/pagination');
+ require('controllers/main/host');*/
+
+ describe('MainAppsController', function () {
+
+
+   describe('#iTotalDisplayRecordsObserver()', function () {
+     it('should set number of filtered jobs when switching to all jobs', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("paginationObject.iTotalDisplayRecords", 5);
+       expect(mainAppsController.get('filterObject.filteredDisplayRecords')).to.equal(5);
+     })
+   });
+
+
+   describe('#filterObject.onRunTypeChange()', function () {
+     it('should set sSearch_2 of filterObject when changing value of filterObject.runType', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("filterObject.runType", "MapReduce");
+       expect(mainAppsController.get('filterObject.sSearch_2')).to.equal("mr");
+       mainAppsController.set("filterObject.runType", "Hive");
+       expect(mainAppsController.get('filterObject.sSearch_2')).to.equal("hive");
+       mainAppsController.set("filterObject.runType", "Pig");
+       expect(mainAppsController.get('filterObject.sSearch_2')).to.equal("pig");
+     })
+   });
+
+   describe('#filterObject.onJobsChange()', function () {
+     it('should set minJobs,maxJobs of filterObject when changing value of filterObject.jobs', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("filterObject.jobs", ">3");
+       expect(mainAppsController.get('filterObject.minJobs')).to.equal("3");
+       expect(mainAppsController.get('filterObject.maxJobs')).to.equal("");
+       mainAppsController.set("filterObject.jobs", "<3");
+       expect(mainAppsController.get('filterObject.minJobs')).to.equal("");
+       expect(mainAppsController.get('filterObject.maxJobs')).to.equal("3");
+       mainAppsController.set("filterObject.jobs", "3");
+       expect(mainAppsController.get('filterObject.minJobs')).to.equal("3");
+       expect(mainAppsController.get('filterObject.maxJobs')).to.equal("3");
+       mainAppsController.set("filterObject.jobs", "=3");
+       expect(mainAppsController.get('filterObject.minJobs')).to.equal("3");
+       expect(mainAppsController.get('filterObject.maxJobs')).to.equal("3");
+     })
+   });
+
+   describe('#filterObject.onInputChange()', function () {
+     it('should set minInputBytes,maxInputBytes of filterObject when changing value of filterObject.input', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("filterObject.input", ">3gb");
+       expect(mainAppsController.get('filterObject.minInputBytes')).to.equal(3215856763);
+       expect(mainAppsController.get('filterObject.maxInputBytes')).to.equal("");
+       mainAppsController.set("filterObject.input", "<6m");
+       expect(mainAppsController.get('filterObject.minInputBytes')).to.equal("");
+       expect(mainAppsController.get('filterObject.maxInputBytes')).to.equal(6343884);
+       mainAppsController.set("filterObject.input", "10kb");
+       expect(mainAppsController.get('filterObject.minInputBytes')).to.equal(10189);
+       expect(mainAppsController.get('filterObject.maxInputBytes')).to.equal(10291);
+       mainAppsController.set("filterObject.input", "1");
+       expect(mainAppsController.get('filterObject.minInputBytes')).to.equal(1024);
+       expect(mainAppsController.get('filterObject.maxInputBytes')).to.equal(1075);
+     })
+   });
+
+   describe('#filterObject.onOutputChange()', function () {
+     it('should set minOutputBytes,maxOutputBytes of filterObject when changing value of filterObject.output', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("filterObject.output", ">3gb");
+       expect(mainAppsController.get('filterObject.minOutputBytes')).to.equal(3215856763);
+       expect(mainAppsController.get('filterObject.maxOutputBytes')).to.equal("");
+       mainAppsController.set("filterObject.output", "<6m");
+       expect(mainAppsController.get('filterObject.minOutputBytes')).to.equal("");
+       expect(mainAppsController.get('filterObject.maxOutputBytes')).to.equal(6343884);
+       mainAppsController.set("filterObject.output", "10kb");
+       expect(mainAppsController.get('filterObject.minOutputBytes')).to.equal(10189);
+       expect(mainAppsController.get('filterObject.maxOutputBytes')).to.equal(10291);
+       mainAppsController.set("filterObject.output", "1");
+       expect(mainAppsController.get('filterObject.minOutputBytes')).to.equal(1024);
+       expect(mainAppsController.get('filterObject.maxOutputBytes')).to.equal(1075);
+     })
+   });
+
+   describe('#filterObject.onDurationChange()', function () {
+     it('should set minDuration,maxDuration of filterObject when changing value of filterObject.duration', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("filterObject.duration", ">3h");
+       expect(mainAppsController.get('filterObject.minDuration')).to.equal(10799640);
+       expect(mainAppsController.get('filterObject.maxDuration')).to.equal("");
+       mainAppsController.set("filterObject.duration", "<6m");
+       expect(mainAppsController.get('filterObject.minDuration')).to.equal("");
+       expect(mainAppsController.get('filterObject.maxDuration')).to.equal(360060);
+       mainAppsController.set("filterObject.duration", "10s");
+       expect(mainAppsController.get('filterObject.minDuration')).to.equal(9990);
+       expect(mainAppsController.get('filterObject.maxDuration')).to.equal(10010);
+       mainAppsController.set("filterObject.duration", "1");
+       expect(mainAppsController.get('filterObject.minDuration')).to.equal(990);
+       expect(mainAppsController.get('filterObject.maxDuration')).to.equal(1010);
+     })
+   });
+
+   describe('#filterObject.onRunDateChange()', function () {
+     it('should set minStartTime,maxStartTime of filterObject when changing value of filterObject.runDate', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("filterObject.runDate", "Any");
+       expect(mainAppsController.get('filterObject.minStartTime')).to.equal("");
+       mainAppsController.set("filterObject.runDate", "Past 1 Day");
+       expect(mainAppsController.get('filterObject.minStartTime')).to.be.within(((new Date().getTime())-86400000)-10,((new Date().getTime())-86400000)+10);
+       mainAppsController.set("filterObject.runDate", "Past 2 Days");
+       expect(mainAppsController.get('filterObject.minStartTime')).to.be.within(((new Date().getTime())-172800000)-10,((new Date().getTime())-172800000)+10);
+       mainAppsController.set("filterObject.runDate", "Past 7 Days");
+       expect(mainAppsController.get('filterObject.minStartTime')).to.be.within(((new Date().getTime())-604800000)-10,((new Date().getTime())-604800000)+10);
+       mainAppsController.set("filterObject.runDate", "Past 14 Days");
+       expect(mainAppsController.get('filterObject.minStartTime')).to.be.within(((new Date().getTime())-1209600000)-10,((new Date().getTime())-1209600000)+10);
+       mainAppsController.set("filterObject.runDate", "Past 30 Days");
+       expect(mainAppsController.get('filterObject.minStartTime')).to.be.within(((new Date().getTime())-2592000000)-10,((new Date().getTime())-2592000000)+10);
+     })
+   });
+
+   describe('#filterObject.createAppLink(), #filterObject.valueObserver()', function () {
+     it('should set runUrl of filterObject when changing value for any filter', function () {
+       var mainAppsController = App.MainAppsController.create();
+       mainAppsController.set("filterObject.sSearch_0", "0");
+       mainAppsController.set("filterObject.sSearch_1", "workflowName");
+       mainAppsController.set("filterObject.sSearch_2", "pig");
+       mainAppsController.set("filterObject.sSearch_3", "admin");
+       mainAppsController.set("filterObject.minJobs", "1");
+       mainAppsController.set("filterObject.maxJobs", "2");
+       mainAppsController.set("filterObject.minInputBytes", "2000");
+       mainAppsController.set("filterObject.maxInputBytes", "3000");
+       mainAppsController.set("filterObject.minOutputBytes", "1500");
+       mainAppsController.set("filterObject.maxOutputBytes", "2000");
+       mainAppsController.set("filterObject.minDuration", "1000");
+       mainAppsController.set("filterObject.maxDuration", "2000");
+       mainAppsController.set("filterObject.minStartTime", "999");
+       mainAppsController.set("filterObject.maxStartTime", "1000");
+       mainAppsController.set("filterObject.sSearch", "searchTerm");
+       mainAppsController.set("filterObject.iDisplayLength", "10");
+       mainAppsController.set("filterObject.iDisplayStart", "10");
+       mainAppsController.set("filterObject.iSortCol_0", "1");
+       mainAppsController.set("filterObject.sSortDir_0", "ASC");
+       expect(mainAppsController.get('runUrl')).to.equal("/jobhistory/datatable?" +
+           "sSearch_0=0" +
+           "&sSearch_1=workflowName" +
+           "&sSearch_2=pig" +
+           "&sSearch_3=admin" +
+           "&minJobs=1" +
+           "&maxJobs=2" +
+           "&minInputBytes=2000" +
+           "&maxInputBytes=3000" +
+           "&minOutputBytes=1500" +
+           "&maxOutputBytes=2000" +
+           "&minDuration=1000" +
+           "&maxDuration=2000" +
+           "&minStartTime=999" +
+           "&maxStartTime=1000" +
+           "&sSearch=searchTerm" +
+           "&iDisplayLength=10" +
+           "&iDisplayStart=10" +
+           "&iSortCol_0=1" +
+           "&sSortDir_0=ASC");
+     })
+   });
+
+
+ });
+

Added: incubator/ambari/trunk/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js?rev=1454639&view=auto
==============================================================================
--- incubator/ambari/trunk/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js (added)
+++ incubator/ambari/trunk/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js Sat Mar  9 02:03:51 2013
@@ -0,0 +1,44 @@
+/**
+ * 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('controllers/main/charts/heatmap_metrics/heatmap_metric');
+
+describe('MainChartHeatmapMetric', function () {
+
+  var mainChartHeatmapMetric = App.MainChartHeatmapMetric.create({});
+
+  describe('#formatLegendNumber', function () {
+    var tests = [
+      {m:'undefined to undefined',i:undefined,e:undefined},
+      {m:'0 to 0',i:0,e:0},
+      {m:'1 to 1',i:1,e:1},
+      {m:'1.23 to 1.2',i:1.23,e:'1.2'}
+    ];
+    tests.forEach(function(test) {
+      it(test.m + ' ', function () {
+        expect(mainChartHeatmapMetric.formatLegendNumber(test.i)).to.equal(test.e);
+      });
+    });
+    it('NaN to NaN' + ' ', function () {
+      expect(isNaN(mainChartHeatmapMetric.formatLegendNumber(NaN))).to.equal(true);
+    });
+  })
+
+
+
+});

Modified: incubator/ambari/trunk/ambari-web/test/installer/step1_test.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/test/installer/step1_test.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/test/installer/step1_test.js (original)
+++ incubator/ambari/trunk/ambari-web/test/installer/step1_test.js Sat Mar  9 02:03:51 2013
@@ -19,8 +19,7 @@
 var App = require('app');
 require('controllers/wizard/step1_controller');
 
-/*
-describe('App.InstallerStep1Controller', function () {
+/*describe('App.InstallerStep1Controller', function () {
 
   describe('#validateStep1()', function () {
     it('should return false and sets invalidClusterName to true if cluster name is empty', function () {
@@ -51,3 +50,26 @@ describe('App.InstallerStep1Controller',
   })
 
 })*/
+
+describe('App.WizardStep1Controller', function () {
+
+  var wizardStep1Controller = App.WizardStep1Controller.create();
+
+  describe('#invalidClusterName', function () {
+    it('should return true if no cluster name is present', function () {
+      wizardStep1Controller.set('hasSubmitted', true);
+      wizardStep1Controller.set('content', {'cluster':{'name':''}});
+      expect(wizardStep1Controller.get('invalidClusterName')).to.equal(true);
+    })
+    it('should return true if cluster name contains white spaces', function () {
+      wizardStep1Controller.set('hasSubmitted', true);
+      wizardStep1Controller.set('content', {'cluster':{'name':'the cluster'}});
+      expect(wizardStep1Controller.get('invalidClusterName')).to.equal(true);
+    })
+    it('should return true if cluster name contains special chars', function () {
+      wizardStep1Controller.set('hasSubmitted', true);
+      wizardStep1Controller.set('content', {'cluster':{'name':'$cluster'}});
+      expect(wizardStep1Controller.get('invalidClusterName')).to.equal(true);
+    })
+  })
+})
\ No newline at end of file

Modified: incubator/ambari/trunk/ambari-web/test/installer/step2_test.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/test/installer/step2_test.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/test/installer/step2_test.js (original)
+++ incubator/ambari/trunk/ambari-web/test/installer/step2_test.js Sat Mar  9 02:03:51 2013
@@ -19,144 +19,238 @@
 var App = require('app');
 var Ember = require('ember');
 require('controllers/wizard/step2_controller');
+require('models/host');
 
-describe.skip('App.WizardStep2Controller', function () {
+describe('App.WizardStep2Controller', function () {
 
-  /*describe('#hostsError()', function () {
+  describe('#updateHostNameArr()', function () {
 
-    it('should return t(installer.step2.hostName.error.required) if manualInstall is false, hostNames is empty, and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('manualInstall', false);
-      controller.set('hostNames', '');
-      controller.set('hasSubmitted', true);
-      expect(controller.get('hostsError')).to.equal(Ember.I18n.t('installer.step2.hostName.error.required'));
+      var controller = App.WizardStep2Controller.create({
+        hostNames: 'apache.ambari'
+      });
+      App.store.load(App.Host, {'host_name': 'apache.ambari', id: '1'});
+      controller.updateHostNameArr();
+
+      it('should push to hostNameArr only new host names', function(){
+        expect(controller.get('hostNameArr').length).to.equal(0);
+      })
+
+      it('should push to inputtedAgainHostNames already installed host names', function(){
+        expect(controller.get('inputtedAgainHostNames').length).to.equal(1);
+      })
+  })
+
+  describe('#isAllHostNamesValid()', function () {
+
+    var controller = App.WizardStep2Controller.create({
+      hostNames: ''
+    });
+
+    it('should return true if all host names are valid', function(){
+      controller.set('hostNames', 'amache ambari');
+      expect(controller.isAllHostNamesValid()).to.equal(true);
     })
 
-    it('should return null if manualInstall is false, hostNames is not empty, and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('manualInstall', false);
-      controller.set('hostNames', 'ambari');
-      controller.set('hasSubmitted', true);
+    /*it('should return false if there is invalid host names', function(){
+      controller.set('hostNames', 'amache #@$ ambari');
+      expect(controller.isAllHostNamesValid()).to.equal(false);
+    })*/
+  })
+
+  describe('#checkHostError()', function () {
+
+    var controller = App.WizardStep2Controller.create();
+
+    it('should set hostsError if hostNames is ""', function () {
+      controller.set('content', {'installOptions': {'hostNames': ''}});
+      controller.checkHostError();
+      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();
       expect(controller.get('hostsError')).to.equal(null);
     })
+  })
+
+  describe('#checkHostAfterSubmitHandler()', function () {
 
-    it('should return t(installer.step2.hostName.error.invalid) if manualInstall is false and hostNames has an element ' +
-      'that starts with a hyphen', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('manualInstall', false);
-      controller.set('hostNames', "-apache");
-      expect(controller.get('hostsError')).to.equal(Ember.I18n.t('installer.step2.hostName.error.invalid'));
-    })
-
-    it('should return t(installer.step2.hostName.error.invalid) if manualInstall is false and hostNames has an element ' +
-      'that ends with a hyphen', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('manualInstall', false);
-      controller.set('hostNames', 'apache-');
-      expect(controller.get('hostsError')).to.equal(Ember.I18n.t('installer.step2.hostName.error.invalid'));
-    })
-
-    it('should return t(installer.step2.hostName.error.required) if manualInstall is true, hostNames is empty, and ' +
-      'hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('manualInstall', true);
-      controller.set('hostNames', '');
+    it('should be called after changing hasSubmitted', function (done) {
+      var controller = App.WizardStep2Controller.create({
+        checkHostError: function () {
+          done();
+        }
+      });
       controller.set('hasSubmitted', true);
-      expect(controller.get('hostsError')).to.equal(Ember.I18n.t('installer.step2.hostName.error.required'));
     })
 
+    it('should be called after changing hostNames', function (done) {
+      var controller = App.WizardStep2Controller.create({
+        hasSubmitted: true,
+        checkHostError: function () {
+          done();
+        }
+      });
+      controller.set('content', {'installOptions': {'hostNames': 'ambari'}});
+    })
   })
 
-  describe('#sshKeyError()', function () {
-    it('should return t(installer.step2.sshKey.error.required) to true if manualInstall is false, sshKey is empty, ' +
-      'and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('manualInstall', false);
-      controller.set('sshKey', '');
-      controller.set('hasSubmitted', true);
-      expect(controller.get('sshKeyError')).to.equal(Ember.I18n.t('installer.step2.sshKey.error.required'));
+  describe('#sshKeyError', function () {
+
+    var controller = App.WizardStep2Controller.create({
+      manualInstall: false,
+      sshKey: '',
+      hasSubmitted: true
+    });
+
+    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);
     })
 
-    it('should return null if manualInstall is true, sshKey is empty, and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('sshKey', '');
-      controller.set('manualInstall', true);
-      controller.set('hasSubmitted', true);
+    it('should return null if hasSubmitted is false', function () {
+      controller.set('hasSubmitted', false);
       expect(controller.get('sshKeyError')).to.equal(null);
     })
+  })
 
-    it('should return null if sshKey is not null and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('sshKey', 'ambari');
-      controller.set('hasSubmitted', true);
-      expect(controller.get('sshKeyError')).to.equal(null);
+  describe('#getHostInfo()', function () {
+
+    it('should return object with bootStatus, installType and name for every element in hostNameArr', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNameArr: ['apache', 'ambari'],
+        installType: 'manualDriven'
+      });
+
+      var test = controller.getHostInfo();
+      expect(test).to.eql({
+        'apache':{'name':'apache', 'installType': 'manualDriven', 'bootStatus': 'PENDING'},
+        'ambari':{'name':'ambari', 'installType': 'manualDriven', 'bootStatus': 'PENDING'}
+      });
     })
+  })
 
-  })*/
-    /* Passphrase has been disabled, so commenting out tests
-    it('should set passphraseMatchErr to true if ' +
-      'passphrase and confirmPassphrase doesn\'t match ', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('manualInstall', false);
-      controller.set('passphrase', 'apache ambari');
-      controller.set('confirmPassphrase', 'ambari');
-      controller.validateStep2();
-      expect(controller.get('passphraseMatchErr')).to.equal(true);
-    })
-
-    it('should set passphraseMatchErr to false if passphrase and ' +
-      'confirmPassphrase doesn\'t match but manualInstall is true ', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('passphrase', 'apache ambari');
-      controller.set('confirmPassphrase', 'ambari');
-      controller.set('manualInstall', true);
-      controller.validateStep2();
-      expect(controller.get('passphraseMatchErr')).to.equal(false);
-    })
-
-    it('should set passphraseMatchErr to true if passphrase and ' +
-      'confirmPassphrase matches', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('passphrase', 'apache ambari');
-      controller.set('confirmPassphrase', 'apache ambari');
-      controller.validateStep2();
-      expect(controller.get('passphraseMatchErr')).to.equal(false);
-    })
-    */
-
-  /*describe('#localRepoError()', function() {
-
-    it('should return t(installer.step2.localRepo.error.required) localRepo is true, localRepoPath is empty, and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('localRepo', true);
-      controller.set('localRepoPath', '');
-      controller.set('hasSubmitted', true);
-      expect(controller.get('localRepoError')).to.equal(Ember.I18n.t('installer.step2.localRepo.error.required'));
+  describe('#setSshKey()', function () {
+
+    it('should set content.installOptions.sshKey', function () {
+      var controller = App.WizardStep2Controller.create({
+       content: {'installOptions': {'sshKey': '111'}}
+      });
+      controller.setSshKey('222');
+      expect(controller.get('content.installOptions.sshKey')).to.equal('222');
     })
+  })
 
-    it('should return null if localRepo is true, localRepoPath is not empty, and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('localRepo', true);
-      controller.set('localRepoPath', '/etc/');
-      controller.set('hasSubmitted', true);
-      expect(controller.get('localRepoError')).to.equal(null);
+  describe('#evaluateStep()', function () {
+
+    it('should return false if isSubmitDisabled is true', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNames: 'apache.ambari'
+      });
+      controller.set('isSubmitDisabled', true);
+      expect(controller.evaluateStep()).to.equal(false);
     })
 
-    it('should return null if localRepo is false, localRepoPath is empty, and hasSubmitted is true', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('localRepo', false);
-      controller.set('localRepoPath', '');
-      controller.set('hasSubmitted', true);
-      expect(controller.get('localRepoError')).to.equal(null);
+    it('should return false if hostsError is not empty', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNames: 'apache.ambari'
+      });
+      controller.set('hostsError', 'error');
+      expect(controller.evaluateStep()).to.equal(false);
+    })
+
+    it('should return false if sshKeyError is not empty', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNames: 'apache.ambari'
+      });
+      controller.set('sshKeyError', 'error');
+      expect(controller.evaluateStep()).to.equal(false);
+    })
+
+    it('should return false if hostNameArr is empty', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNames: ''
+      });
+      expect(controller.evaluateStep()).to.equal(false);
+    })
+
+    it('should return false if isPattern is false', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNames: 'apache.ambari',
+        isPattern: false
+      });
+      expect(controller.evaluateStep()).to.equal(false);
     })
   })
 
-  describe('#evaluateStep2(): On hitting step2 \"next\" button', function () {
-    it('should return false if isSubmitDisabled is true ', function () {
-      var controller = App.InstallerStep2Controller.create();
-      controller.set('isSubmitDisabled', true);
-      expect(controller.evaluateStep2()).to.equal(false);
+  describe('#patternExpression()', function () {
+
+    it('should parse hosts from pattern expression to hostNameArr', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNames: 'hots[0-10]'
+      });
+      controller.patternExpression();
+      var result = true;
+      var hosts = controller.get('hostNameArr');
+      for (var i = 0; i<11; i++) {
+        if (hosts[i] !== 'host'+i) {
+          result = false;
+        }
+      }
+      expect(result).to.equal(false);
     })
-  })*/
+  })
+
+  describe('#proceedNext()', function () {
+
+    it('should call manualInstallPopup if manualInstall is true', function (done) {
+      var controller = App.WizardStep2Controller.create({
+        manualInstall: true,
+        manualInstallPopup: function () {
+          done();
+        }
+      });
+      controller.proceedNext();
+    })
+  })
+
+  describe('#isSubmitDisabled', function () {
 
+    var controller = App.WizardStep2Controller.create({
+      hostsError: '',
+      sshKeyError: ''
+    });
+
+    it('should return value if hostsError is not empty', function () {
+      controller.set('hostsError', 'error');
+      expect(controller.get('isSubmitDisabled').length).to.above(0);
+    })
+
+    it('should return value if sshKeyError is not empty', function () {
+      controller.set('sshKeyError', 'error');
+      controller.set('hostsError', '');
+      expect(controller.get('isSubmitDisabled').length).to.above(0);
+    })
+  })
+
+  describe('#saveHosts()', function () {
+
+    it('should set content.hosts', function () {
+      var controller = App.WizardStep2Controller.create({
+        hostNameArr: ['ambari'],
+        content:{'hosts':{}}
+      });
+      App.router = Ember.Object.create({
+        send:function() {}
+      });
+      controller.saveHosts();
+      expect(controller.get('content.hosts')).to.not.be.empty;
+    })
+  })
 })

Modified: incubator/ambari/trunk/ambari-web/test/installer/step4_test.js
URL: http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-web/test/installer/step4_test.js?rev=1454639&r1=1454638&r2=1454639&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-web/test/installer/step4_test.js (original)
+++ incubator/ambari/trunk/ambari-web/test/installer/step4_test.js Sat Mar  9 02:03:51 2013
@@ -20,148 +20,132 @@ var Ember = require('ember');
 var App = require('app');
 require('controllers/wizard/step4_controller');
 
-/*
-describe('App.InstallerStep4Controller', function () {
+describe('App.WizardStep4Controller', function () {
 
-  var DEFAULT_SERVICES = ['HDFS'];
-  var OPTIONAL_SERVICES = ['MAPREDUCE', 'NAGIOS', 'GANGLIA', 'OOZIE', 'HIVE', 'HBASE', 'PIG', 'SQOOP', 'ZOOKEEPER', 'HCATALOG'];
+  var services = [
+    'HDFS', 'MAPREDUCE', 'NAGIOS', 'GANGLIA', 'OOZIE', 'HIVE', 'HBASE', 'PIG', 'SCOOP', 'ZOOKEEPER', 'HCATALOG', 'WEBHCAT'
+  ]
 
-  var controller = App.InstallerStep4Controller.create();
-  controller.rawContent.forEach(function(item){
-    item.isSelected = true;
-    controller.pushObject(Ember.Object.create(item));
-    });
+  var controller = App.WizardStep4Controller.create();
+  services.forEach(function(serviceName, index){
+    controller.pushObject(Ember.Object.create({
+      'serviceName':serviceName, 'isSelected': true, 'isInstalled': false, 'isDisabled': index == 0
+    }));
+  });
 
-  describe('#selectMinimum()', function () {
-    it('should set isSelected is false on all non-default services and isSelected is true on all default services', function() {
-      controller.selectMinimum();
-      DEFAULT_SERVICES.forEach(function (serviceName) {
-        expect(controller.findProperty('serviceName', serviceName).get('isSelected')).to.equal(true);
-      });
-      OPTIONAL_SERVICES.forEach(function (serviceName) {
-        expect(controller.findProperty('serviceName', serviceName).get('isSelected')).to.equal(false);
-      });
+  describe('#isSubmitDisabled', function () {
+    it('should return false if at least one selected service is not installed', function () {
+      expect(controller.get('isSubmitDisabled')).to.equal(false);
     })
-  })
-
-  describe('#selectAll()', function () {
-    it('should set isSelected is true on all non-default services and isSelected is true on all default services', function() {
-      controller.selectAll();
-      DEFAULT_SERVICES.forEach(function (serviceName) {
-        expect(controller.findProperty('serviceName', serviceName).get('isSelected')).to.equal(true);
-      });
-      OPTIONAL_SERVICES.forEach(function (serviceName) {
-        expect(controller.findProperty('serviceName', serviceName).get('isSelected')).to.equal(true);
-      });
+    it('should return true if all selected services are already installed', function () {
+      controller.setEach('isInstalled', true);
+      controller.findProperty('serviceName', 'HDFS').set('isSelected', false);
+      expect(controller.get('isSubmitDisabled')).to.equal(true);
     })
   })
 
-  describe('#isAll()', function () {
-
-    beforeEach(function() {
-      DEFAULT_SERVICES.forEach(function(serviceName) {
-        controller.findProperty('serviceName', serviceName).set('isSelected', true);
-      });
-      OPTIONAL_SERVICES.forEach(function(serviceName) {
-        controller.findProperty('serviceName', serviceName).set('isSelected', true);
-      });
-    });
-
-    it('should return true if isSelected is true for all services', function() {
+  describe('#isAll', function () {
+    it('should return true if all services are selected', function () {
+      controller.findProperty('serviceName', 'HDFS').set('isSelected', true);
       expect(controller.get('isAll')).to.equal(true);
     })
 
-    it('should return false if isSelected is false for one of the services', function() {
-      controller.findProperty('serviceName', 'HBASE').set('isSelected', false);
+    it('should return false if at least one service is not selected', function () {
+      controller.findProperty('serviceName', 'HDFS').set('isSelected', false);
       expect(controller.get('isAll')).to.equal(false);
     })
   })
 
-  describe('#isMinimum()', function () {
-
-    beforeEach(function() {
-      DEFAULT_SERVICES.forEach(function(serviceName) {
-        controller.findProperty('serviceName', serviceName).set('isSelected', true);
-      });
-      OPTIONAL_SERVICES.forEach(function(serviceName) {
-        controller.findProperty('serviceName', serviceName).set('isSelected', false);
-      });
-    });
-
-    it('should return true if isSelected is true for all default services and isSelected is false for all optional services', function() {
+  describe('#isMinimum', function () {
+    it('should return true if there are no services selected, except disabled', function () {
+      controller.setEach('isSelected', false);
       expect(controller.get('isMinimum')).to.equal(true);
     })
 
-    it('should return false if isSelected is true for all default serices and isSelected is true for one of optional services', function() {
-      controller.findProperty('serviceName', 'HBASE').set('isSelected', true);
+    it('should return false if at least one service is selected, except disabled', function () {
+      controller.findProperty('serviceName', 'MAPREDUCE').set('isSelected', true);
       expect(controller.get('isMinimum')).to.equal(false);
     })
+  })
+
+  describe('#checkDependencies()', function () {
+    it('should set ZooKeeper isSelected property like in HBase', function () {
+      controller.setEach('isSelected', false);
+      controller.findProperty('serviceName', 'HBASE').set('isSelected', true);
+      controller.checkDependencies();
+      expect(controller.findProperty('serviceName', 'ZOOKEEPER').get('isSelected')).to.equal(true);
+    })
+    it('should set ZooKeeper, HCatalog, WebHCatalog isSelected property like in Hive', function () {
+      controller.setEach('isSelected', false);
+      controller.findProperty('serviceName', 'HIVE').set('isSelected', true);
+      controller.checkDependencies();
+      expect(controller.findProperty('serviceName', 'ZOOKEEPER').get('isSelected')).to.equal(true);
+      expect(controller.findProperty('serviceName', 'HCATALOG').get('isSelected')).to.equal(true);
+      expect(controller.findProperty('serviceName', 'WEBHCAT').get('isSelected')).to.equal(true);
+    })
+  })
+
+  describe('#selectAll()', function () {
+    it('should select all services', function () {
+      controller.setEach('isSelected', false);
+      controller.selectAll();
+      expect(controller.everyProperty('isSelected', true)).to.equal(true);
+    })
+  })
 
+  describe('#selectMinimum()', function () {
+    it('should set isSelected false for all not disabled services', function () {
+      controller.setEach('isSelected', true);
+      controller.selectMinimum();
+      expect(controller.findProperty('serviceName', 'HDFS').get('isSelected')).to.equal(true);
+      expect(controller.filterProperty('isDisabled', false).everyProperty('isSelected', false)).to.equal(true);
+    })
   })
 
-  describe('#needToAddMapReduce', function() {
+  describe('#needToAddMapReduce()', function () {
+    it('should return true if Pig is selected and MapReduce is not selected', function () {
+      controller.setEach('isSelected', false);
+      controller.findProperty('serviceName', 'PIG').set('isSelected', true);
+      expect(controller.needToAddMapReduce()).to.equal(true);
+    })
 
-    describe('mapreduce not selected', function() {
-      beforeEach(function() {
-        controller.findProperty('serviceName', 'MAPREDUCE').set('isSelected', false);
-      })
-
-      it('should return true if Hive is selected and MapReduce is not selected', function() {
-        controller.findProperty('serviceName', 'HIVE').set('isSelected', true);
-        expect(controller.needToAddMapReduce()).to.equal(true);
-      })
-      it('should return true if Pig is selected and MapReduce is not selected', function() {
-        controller.findProperty('serviceName', 'PIG').set('isSelected', true);
-        expect(controller.needToAddMapReduce()).to.equal(true);
-      })
-      it('should return true if Oozie is selected and MapReduce is not selected', function() {
-        controller.findProperty('serviceName', 'OOZIE').set('isSelected', true);
-        expect(controller.needToAddMapReduce()).to.equal(true);
-      })
-    })
-
-    describe('mapreduce not selected', function() {
-      beforeEach(function() {
-        controller.findProperty('serviceName', 'MAPREDUCE').set('isSelected', true);
-      })
-
-      it('should return false if Hive is selected and MapReduce is selected', function() {
-        controller.findProperty('serviceName', 'HIVE').set('isSelected', true);
-        expect(controller.needToAddMapReduce()).to.equal(false);
-      })
-      it('should return false if Pig is selected and MapReduce is not selected', function() {
-        controller.findProperty('serviceName', 'PIG').set('isSelected', true);
-        expect(controller.needToAddMapReduce()).to.equal(false);
-      })
-      it('should return false if Oozie is selected and MapReduce is not selected', function() {
-        controller.findProperty('serviceName', 'OOZIE').set('isSelected', true);
-        expect(controller.needToAddMapReduce()).to.equal(false);
-      })
-    })
-
-  })
-
-  describe('#saveSelectedServiceNamesToDB', function() {
-
-    beforeEach(function() {
-      DEFAULT_SERVICES.forEach(function(serviceName) {
-        controller.findProperty('serviceName', serviceName).set('isSelected', true);
-      });
-      OPTIONAL_SERVICES.forEach(function(serviceName) {
-        controller.findProperty('serviceName', serviceName).set('isSelected', true);
-      });
-    });
-
-    it('should store the selected service names in App.db.selectedServiceNames', function() {
-      App.db.setLoginName('tester');
-      App.db.setClusterName('test');
-      controller.saveSelectedServiceNamesToDB();
-      // console.log('controller length=' + controller.get('length'));
-      var selectedServiceNames = App.db.getSelectedServiceNames();
-      // console.log('service length=' + selectedServiceNames.get('length'));
-      expect(selectedServiceNames.length === DEFAULT_SERVICES.length + OPTIONAL_SERVICES.length).to.equal(true);
+    it('should return true if Oozie is selected and MapReduce is not selected', function () {
+      controller.setEach('isSelected', false);
+      controller.findProperty('serviceName', 'OOZIE').set('isSelected', true);
+      expect(controller.needToAddMapReduce()).to.equal(true);
     })
 
+    it('should return true if Hive is selected and MapReduce is not selected', function () {
+      controller.setEach('isSelected', false);
+      controller.findProperty('serviceName', 'HIVE').set('isSelected', true);
+      expect(controller.needToAddMapReduce()).to.equal(true);
+    })
+
+    it('should return false if MapReduce is selected or Pig, Oozie and Hive are not selected', function () {
+      controller.findProperty('serviceName', 'MAPREDUCE').set('isSelected', true);
+      expect(controller.needToAddMapReduce()).to.equal(false);
+      controller.setEach('isSelected', false);
+      expect(controller.needToAddMapReduce()).to.equal(false);
+    })
+  })
+
+  describe('#gangliaOrNagiosNotSelected()', function () {
+    it('should return true if Nagios or Ganglia is not selected', function () {
+      controller.setEach('isSelected', true);
+      controller.findProperty('serviceName', 'NAGIOS').set('isSelected', false);
+      expect(controller.gangliaOrNagiosNotSelected()).to.equal(true);
+      controller.setEach('isSelected', true);
+      controller.findProperty('serviceName', 'GANGLIA').set('isSelected', false);
+      expect(controller.gangliaOrNagiosNotSelected()).to.equal(true);
+    })
+
+    it('should return false if Nagios and Ganglia is selected', function () {
+      controller.setEach('isSelected', false);
+      controller.findProperty('serviceName', 'GANGLIA').set('isSelected', true);
+      controller.findProperty('serviceName', 'NAGIOS').set('isSelected', true);
+      expect(controller.gangliaOrNagiosNotSelected()).to.equal(false);
+    })
   })
 
-})*/
+})
+