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

git commit: AMBARI-6953 Cover Config History functionality with unit tests. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 9f7c878c9 -> 3107ac10f


AMBARI-6953 Cover Config History functionality with unit tests. (atkach)


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

Branch: refs/heads/trunk
Commit: 3107ac10fb09316f33c7a8a5d1046bc3bcf5fe4e
Parents: 9f7c878
Author: atkach <at...@hortonworks.com>
Authored: Wed Aug 20 21:20:53 2014 +0300
Committer: atkach <at...@hortonworks.com>
Committed: Wed Aug 20 21:20:53 2014 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   3 +
 .../main/admin/security/add/step2.js            |  16 +-
 .../views/common/configs/config_history_flow.js |  44 +-
 .../views/main/dashboard/config_history_view.js |   6 +-
 .../main/admin/security/add/step2_test.js       |  60 +-
 .../dashboard/config_history_controller_test.js | 164 +++++
 .../main/service/info/config_test.js            |  24 +-
 .../test/controllers/wizard/step4_test.js       |  30 +-
 .../common/configs/config_history_flow_test.js  | 687 +++++++++++++++++++
 .../main/dashboard/config_history_view_test.js  | 240 +++++++
 10 files changed, 1186 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/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 48db27e..985da16 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -54,6 +54,7 @@ var files = ['test/init_model_test',
   'test/controllers/main/admin/security/add/step2_test',
   'test/controllers/main/admin/security/add/step3_test',
   'test/controllers/main/admin/security/add/step4_test',
+  'test/controllers/main/dashboard/config_history_controller_test',
   'test/controllers/main/charts/heatmap_test',
   'test/controllers/main/charts/heatmap_metrics/heatmap_metric_test',
   'test/controllers/main/charts/heatmap_metrics/heatmap_metric_yarn_test',
@@ -130,7 +131,9 @@ var files = ['test/init_model_test',
   'test/views/common/table_view_test',
   'test/views/common/quick_link_view_test',
   'test/views/common/rolling_restart_view_test',
+  'test/views/common/configs/config_history_flow_test',
   'test/views/main/dashboard_test',
+  'test/views/main/dashboard/config_history_view_test',
   'test/views/main/dashboard/widget_test',
   'test/views/main/dashboard/widgets_test',
   'test/views/main/dashboard/widgets/text_widget_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/app/controllers/main/admin/security/add/step2.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/security/add/step2.js b/ambari-web/app/controllers/main/admin/security/add/step2.js
index 857099f..cb90c63 100644
--- a/ambari-web/app/controllers/main/admin/security/add/step2.js
+++ b/ambari-web/app/controllers/main/admin/security/add/step2.js
@@ -344,18 +344,18 @@ App.MainAdminSecurityAddStep2Controller = Em.Controller.extend({
   addUserPrincipals: function (serviceConfigs, securityUsers) {
     var generalService = serviceConfigs.findProperty('serviceName', 'GENERAL').configs;
     this.setUserPrincipalValue(securityUsers.findProperty('name', 'smokeuser'), generalService.findProperty('name', 'smokeuser_principal_name'));
-    var servicesWithUserPrincipals = ['HDFS','HBASE'];
+    var servicesWithUserPrincipals = ['HDFS', 'HBASE'];
 
-    servicesWithUserPrincipals.forEach(function(serviceName){
+    servicesWithUserPrincipals.forEach(function (serviceName) {
       var isServiceInstalled = serviceConfigs.someProperty('serviceName', serviceName);
-      var userPricipal = generalService.findProperty('name', serviceName.toLowerCase()  + '_principal_name');
-      var userKeytab = generalService.findProperty('name', serviceName.toLowerCase()  + '_user_keytab');
-      var userName = securityUsers.findProperty('name', serviceName.toLowerCase()  + '_user');
-      if (isServiceInstalled && this.setUserPrincipalValue(userName, userPricipal)) {
-        userPricipal.isVisible = true;
+      var userPrincipal = generalService.findProperty('name', serviceName.toLowerCase() + '_principal_name');
+      var userKeytab = generalService.findProperty('name', serviceName.toLowerCase() + '_user_keytab');
+      var userName = securityUsers.findProperty('name', serviceName.toLowerCase() + '_user');
+      if (isServiceInstalled && this.setUserPrincipalValue(userName, userPrincipal)) {
+        userPrincipal.isVisible = true;
         userKeytab.isVisible = true;
       }
-    },this);
+    }, this);
   },
   /**
    * set default value of user principal

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/app/views/common/configs/config_history_flow.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/configs/config_history_flow.js b/ambari-web/app/views/common/configs/config_history_flow.js
index 892c778..b6881a5 100644
--- a/ambari-web/app/views/common/configs/config_history_flow.js
+++ b/ambari-web/app/views/common/configs/config_history_flow.js
@@ -62,6 +62,13 @@ App.ConfigHistoryFlowView = Em.View.extend({
     }
     return this.get('displayedServiceVersion.notes');
   }.property('displayedServiceVersion'),
+
+  serviceVersions: function () {
+    var allServiceVersions = App.ServiceConfigVersion.find().filterProperty('serviceName', this.get('serviceName'));
+    return allServiceVersions.sort(function (a, b) {
+      return Em.get(a, 'createTime') - Em.get(b, 'createTime');
+    });
+  }.property('serviceName'),
   /**
    * service versions which in viewport and visible to user
    */
@@ -126,6 +133,7 @@ App.ConfigHistoryFlowView = Em.View.extend({
    */
   keepInfoBarAtTop: function () {
     var defaultTop;
+    var self = this;
     //reset defaultTop value in closure
     $(window).unbind('scroll');
 
@@ -139,17 +147,25 @@ App.ConfigHistoryFlowView = Em.View.extend({
       }
       //290 - default "top" property in px
       defaultTop = defaultTop || (infoBar.get(0).getBoundingClientRect() && infoBar.get(0).getBoundingClientRect().top) || 290;
-
-      if (scrollTop > defaultTop) {
-        infoBar.css('top', '10px');
-      } else if (scrollTop > 0) {
-        infoBar.css('top', (defaultTop - scrollTop) + 'px');
-      } else {
-        infoBar.css('top', 'auto');
-      }
+      self.setInfoBarPosition(infoBar, defaultTop, scrollTop);
     })
   },
   /**
+   * calculate and reset top position of info bar
+   * @param infoBar
+   * @param defaultTop
+   * @param scrollTop
+   */
+  setInfoBarPosition: function (infoBar, defaultTop, scrollTop) {
+    if (scrollTop > defaultTop) {
+      infoBar.css('top', '10px');
+    } else if (scrollTop > 0) {
+      infoBar.css('top', (defaultTop - scrollTop) + 'px');
+    } else {
+      infoBar.css('top', 'auto');
+    }
+  },
+  /**
    *  define the first element in viewport
    *  change visibility of arrows
    */
@@ -200,10 +216,10 @@ App.ConfigHistoryFlowView = Em.View.extend({
       version: this.get('displayedServiceVersion.version'),
       serviceName: this.get('displayedServiceVersion.serviceName')
     });
-    var versionText = event.context? event.context.get('versionText') : this.get('displayedServiceVersion.versionText');
+    var versionText = event.context ? event.context.get('versionText') : this.get('displayedServiceVersion.versionText');
     App.showConfirmationPopup(function () {
-      self.sendRevertCall(serviceConfigVersion);
-    },
+        self.sendRevertCall(serviceConfigVersion);
+      },
       Em.I18n.t('services.service.config.configHistory.makeCurrent.message').format(versionText, this.get('displayedServiceVersion.serviceName'), this.get('displayedServiceVersion.configGroup'))
     );
   },
@@ -273,12 +289,6 @@ App.ConfigHistoryFlowView = Em.View.extend({
       }
     });
   },
-  serviceVersions: function () {
-    var allServiceVersions = App.ServiceConfigVersion.find().filterProperty('serviceName', this.get('serviceName'));
-    return allServiceVersions.sort(function (a, b) {
-      return Em.get(a, 'createTime') - Em.get(b, 'createTime');
-    });
-  }.property('serviceName'),
   /**
    * move back to the previous service version
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/app/views/main/dashboard/config_history_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/config_history_view.js b/ambari-web/app/views/main/dashboard/config_history_view.js
index cf395ba..b7e6049 100644
--- a/ambari-web/app/views/main/dashboard/config_history_view.js
+++ b/ambari-web/app/views/main/dashboard/config_history_view.js
@@ -33,13 +33,13 @@ App.MainConfigHistoryView = App.TableView.extend({
 
   pageContent: function () {
     var content = this.get('filteredContent');
-    if (content.length > this.get('endIndex') - this.get('startIndex') + 1) {
-      content = content.slice(0, this.get('endIndex') - this.get('startIndex') + 1);
+    if (content.length > ((this.get('endIndex') - this.get('startIndex')) + 1)) {
+      content = content.slice(0, (this.get('endIndex') - this.get('startIndex')) + 1);
     }
     return content.sort(function (a, b) {
       return a.get('index') - b.get('index');
     });
-  }.property('filteredCount'),
+  }.property('filteredContent'),
 
   filteredCount: function () {
     return this.get('controller.filteredCount');

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/test/controllers/main/admin/security/add/step2_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/admin/security/add/step2_test.js b/ambari-web/test/controllers/main/admin/security/add/step2_test.js
index 42d63fe..7cf93de 100644
--- a/ambari-web/test/controllers/main/admin/security/add/step2_test.js
+++ b/ambari-web/test/controllers/main/admin/security/add/step2_test.js
@@ -483,41 +483,67 @@ describe('App.MainAdminSecurityAddStep2Controller', function () {
   });
 
   describe('#addUserPrincipals()', function () {
-
+    beforeEach(function () {
+      sinon.stub(controller, 'setUserPrincipalValue', function () {
+        return true;
+      });
+    });
     afterEach(function () {
       controller.setUserPrincipalValue.restore();
     });
 
-    var serviceConfigs = [
+    var generalConfigs = [
       {
         serviceName: 'GENERAL',
         configs: [
-          {name: 'hbase_principal_name'},
-          {name: 'hbase_user_keytab'}
+          {
+            name: 'hbase_principal_name',
+            isVisible: false
+          },
+          {
+            name: 'hbase_user_keytab',
+            isVisible: false
+          },
+          {
+            name: 'hdfs_principal_name',
+            isVisible: false
+          },
+          {
+            name: 'hdfs_user_keytab',
+            isVisible: false
+          }
         ]
       }
     ];
     var securityUsers = [];
 
-    it('HBASE service is not installed', function () {
-      sinon.stub(controller, 'setUserPrincipalValue', Em.K);
+    it('HBASE or HDFS services are not installed neither', function () {
+      var serviceConfigs = generalConfigs.slice(0);
       controller.addUserPrincipals(serviceConfigs, securityUsers);
-      expect(controller.setUserPrincipalValue.calledTwice).to.be.true;
+      expect(serviceConfigs[0].configs.findProperty('name', 'hbase_principal_name').isVisible).to.be.false;
+      expect(serviceConfigs[0].configs.findProperty('name', 'hbase_user_keytab').isVisible).to.be.false;
     });
-    it('HBASE service is installed, setUserPrincipalValue return false', function () {
-      sinon.stub(controller, 'setUserPrincipalValue', function () {
-        return false;
-      });
+    it('HBASE service is installed', function () {
+      var serviceConfigs = generalConfigs.slice(0);
       serviceConfigs.push({serviceName: 'HBASE'});
       controller.addUserPrincipals(serviceConfigs, securityUsers);
-      expect(controller.setUserPrincipalValue.calledThrice).to.be.true;
+      expect(serviceConfigs[0].configs.findProperty('name', 'hbase_principal_name').isVisible).to.be.true;
+      expect(serviceConfigs[0].configs.findProperty('name', 'hbase_user_keytab').isVisible).to.be.true;
+    });
+    it('HDFS service is installed', function () {
+      var serviceConfigs = generalConfigs.slice(0);
+      serviceConfigs.push({serviceName: 'HDFS'});
+      controller.addUserPrincipals(serviceConfigs, securityUsers);
+      expect(serviceConfigs[0].configs.findProperty('name', 'hdfs_principal_name').isVisible).to.be.true;
+      expect(serviceConfigs[0].configs.findProperty('name', 'hdfs_user_keytab').isVisible).to.be.true;
     });
-    it('HBASE service is installed, setUserPrincipalValue return true', function () {
-      sinon.stub(controller, 'setUserPrincipalValue', function () {
-        return true;
-      });
+    it('HDFS and HBASE services are installed', function () {
+      var serviceConfigs = generalConfigs.slice(0);
+      serviceConfigs.push({serviceName: 'HDFS'});
+      serviceConfigs.push({serviceName: 'HBASE'});
       controller.addUserPrincipals(serviceConfigs, securityUsers);
-      expect(controller.setUserPrincipalValue.calledThrice).to.be.true;
+      expect(serviceConfigs[0].configs.findProperty('name', 'hdfs_principal_name').isVisible).to.be.true;
+      expect(serviceConfigs[0].configs.findProperty('name', 'hdfs_user_keytab').isVisible).to.be.true;
       expect(serviceConfigs[0].configs.findProperty('name', 'hbase_principal_name').isVisible).to.be.true;
       expect(serviceConfigs[0].configs.findProperty('name', 'hbase_user_keytab').isVisible).to.be.true;
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/test/controllers/main/dashboard/config_history_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/dashboard/config_history_controller_test.js b/ambari-web/test/controllers/main/dashboard/config_history_controller_test.js
new file mode 100644
index 0000000..257e012
--- /dev/null
+++ b/ambari-web/test/controllers/main/dashboard/config_history_controller_test.js
@@ -0,0 +1,164 @@
+/**
+ * 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/dashboard/config_history_controller');
+
+describe('MainConfigHistoryController', function () {
+
+  var controller = App.MainConfigHistoryController.create();
+
+  describe('#realUrl', function () {
+    it('cluster name is empty', function () {
+      App.set('clusterName', '');
+      expect(controller.get('realUrl')).to.equal('/api/v1/clusters//configurations/serviceconfigversions?<parameters>fields=serviceconfigversion,user,appliedtime,createtime,service_name,service_config_version_note&minimal_response=true');
+    });
+    it('cluster name is "mycluster"', function () {
+      App.set('clusterName', 'mycluster');
+      expect(controller.get('realUrl')).to.equal('/api/v1/clusters/mycluster/configurations/serviceconfigversions?<parameters>fields=serviceconfigversion,user,appliedtime,createtime,service_name,service_config_version_note&minimal_response=true');
+    });
+  });
+  describe('#load()', function () {
+    it('', function () {
+      sinon.stub(controller, 'updateTotalCounter', Em.K);
+      sinon.stub(controller, 'loadCurrentVersions', function(){
+        return {complete: Em.K};
+      });
+
+      controller.load();
+      expect(controller.updateTotalCounter.calledOnce).to.be.true;
+      expect(controller.loadCurrentVersions.calledOnce).to.be.true;
+
+      controller.updateTotalCounter.restore();
+      controller.loadCurrentVersions.restore();
+    });
+  });
+  describe('#loadConfigVersionsToModel()', function () {
+    it('', function () {
+      sinon.stub(App.HttpClient, 'get', Em.K);
+      sinon.stub(controller, 'getUrl', Em.K);
+      sinon.stub(controller, 'getQueryParameters', function(){
+        return [1];
+      });
+
+      controller.loadConfigVersionsToModel();
+      expect(App.HttpClient.get.calledOnce).to.be.true;
+      expect(controller.getQueryParameters.calledOnce).to.be.true;
+      expect(controller.getUrl.calledWith([1])).to.be.true;
+
+
+      controller.getUrl.restore();
+      controller.getQueryParameters.restore();
+      App.HttpClient.get.restore();
+    });
+  });
+  describe('#loadCurrentVersions()', function () {
+    it('', function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+
+      controller.loadCurrentVersions();
+      expect(App.ajax.send.calledOnce).to.be.true;
+
+      App.ajax.send.restore();
+    });
+  });
+  describe('#updateTotalCounter()', function () {
+    it('', function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+
+      controller.updateTotalCounter();
+      expect(App.ajax.send.calledOnce).to.be.true;
+
+      App.ajax.send.restore();
+    });
+  });
+  describe('#loadCurrentVersionsSuccess()', function () {
+    it('', function () {
+      var data = {
+        Clusters: {
+          desired_serviceconfigversions: {
+            'SERVICE': {
+              serviceconfigversion: 1
+            }
+          }
+        }
+      };
+      controller.loadCurrentVersionsSuccess(data);
+      expect(App.cache['currentConfigVersions']).to.eql({SERVICE_1: true});
+    });
+  });
+  describe('#updateTotalCounterSuccess()', function () {
+    it('', function () {
+      controller.updateTotalCounterSuccess({itemTotal: 1});
+      expect(controller.get('totalCount')).to.equal(1);
+    });
+  });
+  describe('#getUrl()', function () {
+    beforeEach(function () {
+      sinon.stub(App.router, 'get', function () {
+        return {
+          computeParameters: function () {
+            return 'params'
+          }
+        }
+      });
+    });
+    afterEach(function () {
+      App.router.get.restore();
+    });
+    it('testMode is true', function () {
+      App.set('testMode', true);
+      expect(controller.getUrl()).to.equal('/data/configurations/service_versions.json');
+    });
+    it('query params is empty', function () {
+      App.set('testMode', false);
+      expect(controller.getUrl()).to.equal('/api/v1/clusters/mycluster/configurations/serviceconfigversions?fields=serviceconfigversion,user,appliedtime,createtime,service_name,service_config_version_note&minimal_response=true');
+    });
+    it('query params is correct', function () {
+      App.set('testMode', false);
+      expect(controller.getUrl({})).to.equal('/api/v1/clusters/mycluster/configurations/serviceconfigversions?paramsfields=serviceconfigversion,user,appliedtime,createtime,service_name,service_config_version_note&minimal_response=true');
+    });
+  });
+  describe('#doPolling()', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'load', function(){
+        return {done: Em.K};
+      });
+      this.clock = sinon.useFakeTimers();
+    });
+    afterEach(function () {
+      this.clock.restore();
+      controller.load.restore();
+    });
+    it('isPolling false', function () {
+      controller.set('isPolling', false);
+      controller.doPolling();
+      this.clock.tick(App.componentsUpdateInterval);
+      expect(controller.load.called).to.be.false;
+    });
+    it('isPolling true', function () {
+      controller.set('isPolling', true);
+      controller.doPolling();
+      this.clock.tick(App.componentsUpdateInterval);
+      expect(controller.load.calledOnce).to.be.true;
+    });
+  });
+});
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/test/controllers/main/service/info/config_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/info/config_test.js b/ambari-web/test/controllers/main/service/info/config_test.js
index a49e298..600ae42 100644
--- a/ambari-web/test/controllers/main/service/info/config_test.js
+++ b/ambari-web/test/controllers/main/service/info/config_test.js
@@ -583,16 +583,8 @@ describe("App.MainServiceInfoConfigsController", function () {
     });
   });
 
-  describe("#doPUTClusterConfigurationSite", function () {
-    var t = {
-      data: "data",
-      request: {
-        Clusters: {
-          desired_config: "data"
-        }
-      }
-    },
-    sc = [
+  describe("#doPUTClusterConfigurationSites", function () {
+      var sc = [
       Em.Object.create({
         configs: [
           Em.Object.create({
@@ -644,25 +636,25 @@ describe("App.MainServiceInfoConfigsController", function () {
       sinon.stub(App.router, 'getClusterName', function() {
         return 'clName';
       });
-      sinon.spy($, "ajax");
+      sinon.stub(App.ajax, "send", Em.K);
     });
     afterEach(function () {
-      $.ajax.restore();
+      App.ajax.send.restore();
       App.router.getClusterName.restore();
     });
     it("ajax request to put clsuter cfg", function () {
       mainServiceInfoConfigsController.set('stepConfigs', sc);
-      expect(mainServiceInfoConfigsController.doPUTClusterConfigurationSite(t.data)).to.equal(mainServiceInfoConfigsController.get("doPUTClusterConfigurationSiteResult"));
-      expect(JSON.parse($.ajax.args[0][0].data)).to.deep.equal(t.request);
+      expect(mainServiceInfoConfigsController.doPUTClusterConfigurationSites([])).to.equal(mainServiceInfoConfigsController.get("doPUTClusterConfigurationSiteResult"));
+      expect(App.ajax.send.calledOnce).to.be.true;
     });
     it('values should be parsed', function () {
       mainServiceInfoConfigsController.set('stepConfigs', sc);
-      mainServiceInfoConfigsController.doPUTClusterConfigurationSite();
+      mainServiceInfoConfigsController.doPUTClusterConfigurationSites([]);
       expect(mainServiceInfoConfigsController.get('stepConfigs')[0].get('configs').mapProperty('value').uniq()).to.eql(['1024']);
     });
     it('values should not be parsed', function () {
       mainServiceInfoConfigsController.set('stepConfigs', scExc);
-      mainServiceInfoConfigsController.doPUTClusterConfigurationSite();
+      mainServiceInfoConfigsController.doPUTClusterConfigurationSites([]);
       expect(mainServiceInfoConfigsController.get('stepConfigs')[0].get('configs').mapProperty('value').uniq()).to.eql(['1024m']);
     });
   });

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/test/controllers/wizard/step4_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step4_test.js b/ambari-web/test/controllers/wizard/step4_test.js
index 507a809..9e54e92 100644
--- a/ambari-web/test/controllers/wizard/step4_test.js
+++ b/ambari-web/test/controllers/wizard/step4_test.js
@@ -116,41 +116,17 @@ describe('App.WizardStep4Controller', function () {
     });
   });
 
-  describe('#noDFSs()', function () {
-    it('should return true if HDFS is not selected and GLUSTERFS is absent', function () {
-      controller.findProperty('serviceName', 'HDFS').set('isSelected', false);
-      expect(controller.noDFSs()).to.equal(true);
-    });
-    it('should return false if HDFS is selected and GLUSTERFS is absent', function () {
-      controller.findProperty('serviceName', 'HDFS').set('isSelected', true);
-      expect(controller.noDFSs()).to.equal(false);
-    });
-    it('should return true if HDFS is not selected and GLUSTERFS is not selected, but present', function () {
-      controller.pushObject(Ember.Object.create({
-        'serviceName':'GLUSTERFS', 'isSelected': false, 'canBeSelected': true, 'isInstalled': false, 'isDisabled': false, 'isDFS': true
-      }));
-      controller.findProperty('serviceName', 'HDFS').set('isSelected', false);
-      expect(controller.noDFSs()).to.equal(true);
-    });
-    it('should return false if HDFS is not selected and GLUSTERFS is selected', function () {
-      controller.findProperty('serviceName', 'GLUSTERFS').set('isSelected', true);
-      expect(controller.noDFSs()).to.equal(false);
-    });
-  });
-
   describe('#multipleDFSs()', function () {
     it('should return true if HDFS is selected and GLUSTERFS is selected', function () {
-      controller.findProperty('serviceName', 'HDFS').set('isSelected', true);
-      controller.findProperty('serviceName', 'GLUSTERFS').set('isSelected', true);
+      controller.set('content', generateSelectedServicesContent(['HDFS', 'GLUSTERFS']));
       expect(controller.multipleDFSs()).to.equal(true);
     });
     it('should return false if HDFS is not selected and GLUSTERFS is selected', function () {
-      controller.findProperty('serviceName', 'HDFS').set('isSelected', false);
+      controller.set('content', generateSelectedServicesContent(['GLUSTERFS']));
       expect(controller.multipleDFSs()).to.equal(false);
     });
     it('should return false if HDFS is selected and GLUSTERFS is not selected', function () {
-      controller.findProperty('serviceName', 'HDFS').set('isSelected', true);
-      controller.findProperty('serviceName', 'GLUSTERFS').set('isSelected', false);
+      controller.set('content', generateSelectedServicesContent(['HDFS']));
       expect(controller.multipleDFSs()).to.equal(false);
     });
   });

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/test/views/common/configs/config_history_flow_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/common/configs/config_history_flow_test.js b/ambari-web/test/views/common/configs/config_history_flow_test.js
new file mode 100644
index 0000000..ba24dab
--- /dev/null
+++ b/ambari-web/test/views/common/configs/config_history_flow_test.js
@@ -0,0 +1,687 @@
+/**
+ * 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/common/configs/config_history_flow');
+
+
+describe('App.ConfigHistoryFlowView', function () {
+
+  var view = App.ConfigHistoryFlowView.create({
+    controller: Em.Object.create({
+      loadSelectedVersion: Em.K,
+      onConfigGroupChange: Em.K,
+      loadStep: Em.K
+    }),
+    displayedServiceVersion: Em.Object.create(),
+    serviceVersions: []
+  });
+
+  describe('#isSaveDisabled', function () {
+    var testCases = [
+      {
+        params: {
+          isSubmitDisabled: false,
+          versionLoaded: true
+        },
+        result: false
+      },
+      {
+        params: {
+          isSubmitDisabled: true,
+          versionLoaded: true
+        },
+        result: true
+      },
+      {
+        params: {
+          isSubmitDisabled: false,
+          versionLoaded: false
+        },
+        result: true
+      },
+      {
+        params: {
+          isSubmitDisabled: true,
+          versionLoaded: false
+        },
+        result: true
+      }
+    ];
+    testCases.forEach(function (test) {
+      it('isSubmitDisabled - ' + test.params.isSubmitDisabled + ', versionLoaded - ' + test.params.versionLoaded, function () {
+        view.set('controller.isSubmitDisabled', test.params.isSubmitDisabled);
+        view.set('controller.versionLoaded', test.params.versionLoaded);
+        expect(view.get('isSaveDisabled')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#showMoreLink', function () {
+    var testCases = [
+      {
+        params: {
+          count: 0
+        },
+        result: false
+      },
+      {
+        params: {
+          count: 100
+        },
+        result: false
+      },
+      {
+        params: {
+          count: 101
+        },
+        result: true
+      }
+    ];
+    testCases.forEach(function (test) {
+      it('notes length - ' + test.params.count, function () {
+        view.set('displayedServiceVersion', Em.Object.create({
+          notes: new Array(test.params.count)
+        }));
+        expect(view.get('showMoreLink')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#shortNotes', function () {
+    it('notes length more than 100', function () {
+      view.set('displayedServiceVersion', Em.Object.create({
+        notes: '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123'
+      }));
+      expect(view.get('shortNotes')).to.equal('1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890...');
+    });
+    it('notes length less than 100', function () {
+      view.set('displayedServiceVersion', Em.Object.create({
+        notes: 'notes'
+      }));
+      expect(view.get('shortNotes')).to.equal('notes');
+    });
+  });
+
+  describe('#visibleServiceVersion', function () {
+    var testCases = [
+      {
+        params: {
+          startIndex: 0,
+          serviceVersions: []
+        },
+        result: []
+      },
+      {
+        params: {
+          startIndex: 0,
+          serviceVersions: [1, 2, 3]
+        },
+        result: [1, 2, 3]
+      },
+      {
+        params: {
+          startIndex: 0,
+          serviceVersions: [1, 2, 3, 4, 5, 6, 7]
+        },
+        result: [1, 2, 3, 4, 5]
+      },
+      {
+        params: {
+          startIndex: 3,
+          serviceVersions: [1, 2, 3, 4, 5, 6, 7]
+        },
+        result: [4, 5, 6, 7]
+      }
+    ];
+    testCases.forEach(function (test) {
+      it('start index - ' + test.params.startIndex + ', serviceVersions length - ' + test.params.serviceVersions.length, function () {
+        view.set('serviceVersions', test.params.serviceVersions);
+        view.set('startIndex', test.params.startIndex);
+        view.propertyDidChange('visibleServiceVersion');
+        expect(view.get('visibleServiceVersion')).to.eql(test.result);
+      });
+    });
+  });
+
+  describe('#versionActionsDisabled', function () {
+    it('versionLoaded is false', function () {
+      view.set('controller.versionLoaded', false);
+      expect(view.get('versionActionsDisabled')).to.be.true;
+    });
+    it('versionLoaded is true', function () {
+      view.set('controller.versionLoaded', true);
+      expect(view.get('versionActionsDisabled')).to.be.false;
+    });
+  });
+
+  describe('#dropDownList', function () {
+    var displayedServiceVersion = {version: 1};
+
+    it('Only one service version is present', function () {
+      view.set('serviceVersions', [displayedServiceVersion]);
+      view.set('displayedServiceVersion', displayedServiceVersion);
+      view.propertyDidChange('dropDownList');
+      expect(view.get('dropDownList')).to.be.empty;
+    });
+    it('Three service version', function () {
+      view.set('serviceVersions', [displayedServiceVersion, {version: 2}, {version: 3}]);
+      view.set('displayedServiceVersion', displayedServiceVersion);
+      view.propertyDidChange('dropDownList');
+      expect(view.get('dropDownList')).to.eql([{version: 3}, {version: 2}]);
+    });
+    it('Seven service version, showFullList is false', function () {
+      view.set('serviceVersions', [
+        displayedServiceVersion,
+        {version: 2},
+        {version: 3},
+        {version: 4},
+        {version: 5},
+        {version: 6},
+        {version: 7},
+        {version: 8}
+      ]);
+      view.set('displayedServiceVersion', displayedServiceVersion);
+      view.set('showFullList', false);
+      view.propertyDidChange('dropDownList');
+      expect(view.get('dropDownList')).to.eql([
+        {version: 8},
+        {version: 7},
+        {version: 6},
+        {version: 5},
+        {version: 4},
+        {version: 3}
+      ]);
+    });
+    it('Seven service version, showFullList is true', function () {
+      view.set('serviceVersions', [
+        displayedServiceVersion,
+        {version: 2},
+        {version: 3},
+        {version: 4},
+        {version: 5},
+        {version: 6},
+        {version: 7},
+        {version: 8}
+      ]);
+      view.set('displayedServiceVersion', displayedServiceVersion);
+      view.set('showFullList', true);
+      view.propertyDidChange('dropDownList');
+      expect(view.get('dropDownList')).to.eql([
+        {version: 8},
+        {version: 7},
+        {version: 6},
+        {version: 5},
+        {version: 4},
+        {version: 3},
+        {version: 2}
+      ]);
+    });
+  });
+
+  describe('#openFullList()', function () {
+    it('', function () {
+      var event = {
+        stopPropagation: Em.K
+      };
+      sinon.spy(event, 'stopPropagation');
+      view.openFullList(event);
+      expect(event.stopPropagation.calledOnce).to.be.true;
+      expect(view.get('showFullList')).to.be.true;
+      event.stopPropagation.restore();
+    });
+  });
+
+  describe('#hideFullList()', function () {
+    var testCases = [
+      {
+        params: {
+          serviceVersions: new Array(0)
+        },
+        result: true
+      },
+      {
+        params: {
+          serviceVersions: new Array(6)
+        },
+        result: true
+      },
+      {
+        params: {
+          serviceVersions: new Array(7)
+        },
+        result: false
+      }
+    ];
+    testCases.forEach(function (test) {
+      it('notes length - ' + test.params.count, function () {
+        view.set('serviceVersions', test.params.serviceVersions);
+        view.hideFullList();
+        expect(view.get('showFullList')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#didInsertElement()', function () {
+    it('', function () {
+      sinon.stub(App, 'tooltip');
+      view.didInsertElement();
+      expect(App.tooltip.calledOnce).to.be.true;
+      App.tooltip.restore();
+    });
+  });
+
+  describe('#willInsertElement()', function () {
+    beforeEach(function () {
+      sinon.stub(view, 'adjustFlowView', Em.K);
+      sinon.stub(view, 'keepInfoBarAtTop', Em.K);
+    });
+    afterEach(function () {
+      view.adjustFlowView.restore();
+      view.keepInfoBarAtTop.restore();
+    });
+    it('Only current version is present', function () {
+      view.set('serviceVersions', [Em.Object.create({isCurrent: true})]);
+
+      view.willInsertElement();
+      expect(view.adjustFlowView.calledOnce).to.be.true;
+      expect(view.keepInfoBarAtTop.calledOnce).to.be.true;
+      expect(view.get('startIndex')).to.equal(0);
+      expect(view.get('serviceVersions').mapProperty('isDisplayed')).to.eql([true]);
+    });
+    it('Five service versions are present', function () {
+      view.set('serviceVersions', [
+        Em.Object.create({isCurrent: true}),
+        Em.Object.create(),
+        Em.Object.create(),
+        Em.Object.create(),
+        Em.Object.create()
+      ]);
+
+      view.willInsertElement();
+      expect(view.adjustFlowView.calledOnce).to.be.true;
+      expect(view.keepInfoBarAtTop.calledOnce).to.be.true;
+      expect(view.get('startIndex')).to.equal(0);
+      expect(view.get('serviceVersions').mapProperty('isDisplayed')).to.eql([true, false, false, false, false]);
+    });
+    it('Six service versions are present', function () {
+      view.set('serviceVersions', [
+        Em.Object.create({isCurrent: true}),
+        Em.Object.create(),
+        Em.Object.create(),
+        Em.Object.create(),
+        Em.Object.create(),
+        Em.Object.create()
+      ]);
+
+      view.willInsertElement();
+      expect(view.adjustFlowView.calledOnce).to.be.true;
+      expect(view.keepInfoBarAtTop.calledOnce).to.be.true;
+      expect(view.get('startIndex')).to.equal(1);
+      expect(view.get('serviceVersions').mapProperty('isDisplayed')).to.eql([true, false, false, false, false, false]);
+    });
+  });
+
+  describe('#setInfoBarPosition()', function () {
+    var testCases = [
+      {
+        params: {
+          scrollTop: 0,
+          defaultTop: 0
+        },
+        result: 'auto'
+      },
+      {
+        params: {
+          scrollTop: 1,
+          defaultTop: 11
+        },
+        result: '10px'
+      },
+      {
+        params: {
+          scrollTop: 1,
+          defaultTop: 0
+        },
+        result: '10px'
+      }
+    ];
+    var infoBar = {
+      css: Em.K
+    };
+    testCases.forEach(function (test) {
+      it('scroll top - ' + test.params.scrollTop + ', default top - ' + test.params.defaultTop, function () {
+        sinon.spy(infoBar, 'css');
+        view.setInfoBarPosition(infoBar, test.params.defaultTop, test.params.scrollTop);
+        expect(infoBar.css.calledWith('top', test.result)).to.be.true;
+        infoBar.css.restore();
+      });
+    });
+  });
+
+  describe('#adjustFlowView()', function () {
+    var testCases = [
+      {
+        params: {
+          serviceVersions: [Em.Object.create()],
+          startIndex: 0
+        },
+        result: {
+          first: [true],
+          showLeftArrow: false,
+          showRightArrow: false
+        }
+      },
+      {
+        params: {
+          serviceVersions: [
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create()
+          ],
+          startIndex: 0
+        },
+        result: {
+          first: [true, false, false, false, false],
+          showLeftArrow: false,
+          showRightArrow: false
+        }
+      },
+      {
+        params: {
+          serviceVersions: [
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create()
+          ],
+          startIndex: 0
+        },
+        result: {
+          first: [true, false, false, false, false, false],
+          showLeftArrow: false,
+          showRightArrow: true
+        }
+      },
+      {
+        params: {
+          serviceVersions: [
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create()
+          ],
+          startIndex: 1
+        },
+        result: {
+          first: [false, true, false, false, false, false],
+          showLeftArrow: true,
+          showRightArrow: false
+        }
+      },
+      {
+        params: {
+          serviceVersions: [
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create(),
+            Em.Object.create()
+          ],
+          startIndex: 1
+        },
+        result: {
+          first: [false, true, false, false, false, false, false],
+          showLeftArrow: true,
+          showRightArrow: true
+        }
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it('start index - ' + test.params.startIndex + ', serviceVersions length - ' + test.params.serviceVersions.length, function () {
+        view.set('startIndex', test.params.startIndex);
+        view.set('serviceVersions', test.params.serviceVersions);
+
+        view.adjustFlowView();
+        expect(view.get('serviceVersions').mapProperty('first')).to.eql(test.result.first);
+        expect(view.get('showLeftArrow')).to.eql(test.result.showLeftArrow);
+        expect(view.get('showRightArrow')).to.eql(test.result.showRightArrow);
+      });
+    });
+  });
+
+  describe('#switchVersion()', function () {
+    var event = {
+      context: Em.Object.create({
+        version: 2
+      })
+    };
+    beforeEach(function(){
+      sinon.stub(view, 'shiftFlowOnSwitch', Em.K);
+      sinon.spy(view.get('controller'), 'loadSelectedVersion');
+    });
+    afterEach(function(){
+      view.shiftFlowOnSwitch.restore();
+      view.get('controller').loadSelectedVersion.restore();
+    });
+    it('Only one service version is present', function () {
+      view.set('serviceVersions', [Em.Object.create({version: 2})]);
+      view.switchVersion(event);
+      expect(view.get('serviceVersions').mapProperty('isDisplayed')).to.eql([true]);
+      expect(view.get('controller').loadSelectedVersion.calledWith(2)).to.be.true;
+      expect(view.shiftFlowOnSwitch.calledWith(0)).to.be.true;
+    });
+    it('Two service versions are present', function () {
+      view.set('serviceVersions', [
+        Em.Object.create({version: 1}),
+        Em.Object.create({version: 2})
+      ]);
+      view.switchVersion(event);
+      expect(view.get('serviceVersions').mapProperty('isDisplayed')).to.eql([false, true]);
+      expect(view.get('controller').loadSelectedVersion.calledWith(2)).to.be.true;
+      expect(view.shiftFlowOnSwitch.calledWith(1)).to.be.true;
+    });
+  });
+
+  describe('#compare()', function () {
+    it('', function () {
+      sinon.spy(view.get('controller'), 'onConfigGroupChange');
+      view.compare({context: {version: 1}});
+
+      expect(view.get('controller.compareServiceVersion')).to.eql({version: 1});
+      expect(view.get('controller').onConfigGroupChange.calledOnce).to.be.true;
+      view.get('controller').onConfigGroupChange.restore();
+    });
+  });
+
+  describe('#revert()', function () {
+    beforeEach(function () {
+      sinon.stub(App, 'showConfirmationPopup', function (callback) {
+        callback();
+      });
+      sinon.stub(view, 'sendRevertCall', Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      view.sendRevertCall.restore();
+    });
+    it('context passed', function () {
+      view.revert({context: Em.Object.create({
+        version: 1,
+        serviceName: 'S1'
+      })});
+
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      expect(view.sendRevertCall.calledWith(Em.Object.create({
+        version: 1,
+        serviceName: 'S1'
+      }))).to.be.true;
+    });
+    it('context is not passed', function () {
+      view.set('displayedServiceVersion', Em.Object.create({
+        version: 1,
+        serviceName: 'S1'
+      }));
+      view.revert({});
+
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      expect(view.sendRevertCall.calledWith(Em.Object.create({
+        version: 1,
+        serviceName: 'S1'
+      }))).to.be.true;
+    });
+  });
+
+  describe('#sendRevertCall()', function () {
+    it('', function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+      view.sendRevertCall(Em.Object.create());
+
+      expect(App.ajax.send.calledOnce).to.be.true;
+      App.ajax.send.restore();
+    });
+  });
+
+  describe('#sendRevertCallSuccess()', function () {
+    it('', function () {
+      sinon.spy(view.get('controller'), 'loadStep');
+      view.sendRevertCallSuccess();
+
+      expect(view.get('controller').loadStep.calledOnce).to.be.true;
+      view.get('controller').loadStep.restore();
+    });
+  });
+
+  describe('#save()', function () {
+    it('', function () {
+      sinon.stub(App.ModalPopup, 'show', Em.K);
+      view.save();
+
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#shiftBack()', function () {
+    it('', function () {
+      sinon.stub(view, 'decrementProperty', Em.K);
+      sinon.stub(view, 'adjustFlowView', Em.K);
+      view.shiftBack();
+
+      expect(view.decrementProperty.calledWith('startIndex')).to.be.true;
+      expect(view.adjustFlowView.calledOnce).to.be.true;
+      view.adjustFlowView.restore();
+      view.decrementProperty.restore();
+    });
+  });
+
+  describe('#shiftForward()', function () {
+    it('', function () {
+      sinon.stub(view, 'incrementProperty', Em.K);
+      sinon.stub(view, 'adjustFlowView', Em.K);
+      view.shiftForward();
+
+      expect(view.incrementProperty.calledWith('startIndex')).to.be.true;
+      expect(view.adjustFlowView.calledOnce).to.be.true;
+      view.adjustFlowView.restore();
+      view.incrementProperty.restore();
+    });
+  });
+
+  describe('#adjustFlowView()', function () {
+    var testCases = [
+      {
+        params: {
+          serviceVersions: [],
+          startIndex: 0,
+          versionIndex: 1
+        },
+        result: {
+          startIndex: 0,
+          adjustFlowViewCall: false
+        }
+      },
+      {
+        params: {
+          serviceVersions: new Array(6),
+          startIndex: 7,
+          versionIndex: 6
+        },
+        result: {
+          startIndex: 1,
+          adjustFlowViewCall: true
+        }
+      },
+      {
+        params: {
+          serviceVersions: new Array(12),
+          startIndex: 7,
+          versionIndex: 6
+        },
+        result: {
+          startIndex: 6,
+          adjustFlowViewCall: true
+        }
+      },
+      {
+        params: {
+          serviceVersions: new Array(12),
+          startIndex: 0,
+          versionIndex: 6
+        },
+        result: {
+          startIndex: 6,
+          adjustFlowViewCall: true
+        }
+      },
+      {
+        params: {
+          serviceVersions: new Array(6),
+          startIndex: 0,
+          versionIndex: 6
+        },
+        result: {
+          startIndex: 1,
+          adjustFlowViewCall: true
+        }
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it('start index - ' + test.params.startIndex + ', serviceVersions length - ' + test.params.serviceVersions.length + ', versionIndex - ' + test.params.versionIndex, function () {
+        sinon.stub(view, 'adjustFlowView', Em.K);
+        view.set('serviceVersions', test.params.serviceVersions);
+        view.set('startIndex', test.params.startIndex);
+        view.shiftFlowOnSwitch(test.params.versionIndex);
+
+        expect(view.get('startIndex')).to.eql(test.result.startIndex);
+        expect(view.adjustFlowView.calledOnce).to.eql(test.result.adjustFlowViewCall);
+        view.adjustFlowView.restore();
+      });
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/3107ac10/ambari-web/test/views/main/dashboard/config_history_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/config_history_view_test.js b/ambari-web/test/views/main/dashboard/config_history_view_test.js
new file mode 100644
index 0000000..b0e8b94
--- /dev/null
+++ b/ambari-web/test/views/main/dashboard/config_history_view_test.js
@@ -0,0 +1,240 @@
+/**
+ * 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/main/dashboard/config_history_view');
+
+describe('App.MainConfigHistoryView', function() {
+  var view = App.MainConfigHistoryView.create({
+    controller: Em.Object.create({
+      name: 'mainConfigHistoryController',
+      paginationProps: [
+        {
+          name: 'displayLength'
+        },
+        {
+          name: 'startIndex'
+        }
+      ],
+      doPolling: Em.K,
+      load: function () {
+        return {done: Em.K};
+      }
+    }),
+    filteredCount: 0
+  });
+  view.removeObserver('controller.resetStartIndex', view, 'resetStartIndex');
+
+  describe('#pageContent', function() {
+    beforeEach(function(){
+      view.propertyDidChange('pageContent');
+    });
+    it('filtered content is empty', function() {
+      view.set('filteredContent', []);
+      expect(view.get('pageContent')).to.be.empty;
+    });
+    it('filtered content contain one item', function() {
+      view.set('filteredCount', 1);
+      view.set('filteredContent', [Em.Object.create()]);
+
+      expect(view.get('pageContent')).to.eql([Em.Object.create()]);
+    });
+    it('filtered content contain two unsorted items', function() {
+      view.set('filteredCount', 2);
+      view.set('filteredContent', [
+        Em.Object.create({index:2}),
+        Em.Object.create({index:1})
+      ]);
+
+      expect(view.get('pageContent')).to.eql([
+        Em.Object.create({index:1}),
+        Em.Object.create({index:2})
+      ]);
+    });
+    it('filtered content contain three items, when two visible', function() {
+      view.set('filteredCount', 2);
+      view.set('filteredContent', [
+        Em.Object.create({index:1}),
+        Em.Object.create({index:2}),
+        Em.Object.create({index:3})
+      ]);
+
+      expect(view.get('pageContent')).to.eql([
+        Em.Object.create({index:1}),
+        Em.Object.create({index:2})
+      ]);
+    });
+  });
+  describe('#updatePagination', function() {
+    beforeEach(function () {
+      sinon.stub(view, 'refresh', Em.K);
+      sinon.stub(App.db, 'setDisplayLength', Em.K);
+      sinon.stub(App.db, 'setStartIndex', Em.K);
+    });
+    afterEach(function () {
+      view.refresh.restore();
+      App.db.setStartIndex.restore();
+      App.db.setDisplayLength.restore();
+    });
+
+    it('displayLength and startIndex are null', function() {
+      view.set('displayLength', null);
+      view.set('startIndex', null);
+
+      view.updatePagination();
+
+      expect(view.refresh.calledOnce).to.be.true;
+      expect(App.db.setStartIndex.called).to.be.false;
+      expect(App.db.setDisplayLength.called).to.be.false;
+      expect(view.get('controller.paginationProps').findProperty('name', 'startIndex').value).to.equal(0);
+      expect(view.get('controller.paginationProps').findProperty('name', 'displayLength').value).to.equal('25');
+    });
+    it('displayLength is correct', function() {
+      view.set('displayLength', '50');
+      view.set('startIndex', null);
+
+      view.updatePagination();
+
+      expect(view.refresh.calledOnce).to.be.true;
+      expect(App.db.setStartIndex.called).to.be.false;
+      expect(App.db.setDisplayLength.calledWith('mainConfigHistoryController', '50')).to.be.true;
+      expect(view.get('controller.paginationProps').findProperty('name', 'startIndex').value).to.equal(0);
+      expect(view.get('controller.paginationProps').findProperty('name', 'displayLength').value).to.equal('50');
+    });
+    it('startIndex is correct', function() {
+      view.set('displayLength', null);
+      view.set('startIndex', 10);
+
+      view.updatePagination();
+
+      expect(view.refresh.calledOnce).to.be.true;
+      expect(App.db.setStartIndex.calledWith('mainConfigHistoryController', 10)).to.be.true;
+      expect(App.db.setDisplayLength.called).to.be.false;
+      expect(view.get('controller.paginationProps').findProperty('name', 'startIndex').value).to.equal(10);
+      expect(view.get('controller.paginationProps').findProperty('name', 'displayLength').value).to.equal('50');
+    });
+    it('displayLength and startIndex are correct', function() {
+      view.set('displayLength', '100');
+      view.set('startIndex', 20);
+
+      view.updatePagination();
+
+      expect(view.refresh.calledOnce).to.be.true;
+      expect(App.db.setStartIndex.calledWith('mainConfigHistoryController', 20)).to.be.true;
+      expect(App.db.setDisplayLength.calledWith('mainConfigHistoryController', '100')).to.be.true;
+      expect(view.get('controller.paginationProps').findProperty('name', 'startIndex').value).to.equal(20);
+      expect(view.get('controller.paginationProps').findProperty('name', 'displayLength').value).to.equal('100');
+    });
+  });
+
+  describe('#didInsertElement()', function() {
+    it('', function() {
+      sinon.stub(view, 'addObserver', Em.K);
+      sinon.spy(view.get('controller'), 'doPolling');
+
+      view.didInsertElement();
+      expect(view.addObserver.calledTwice).to.be.true;
+      expect(view.get('controller.isPolling')).to.be.true;
+      expect(view.get('controller').doPolling.calledOnce).to.be.true;
+
+      view.addObserver.restore();
+      view.get('controller').doPolling.restore();
+    });
+  });
+
+  describe('#willDestroyElement()', function() {
+    it('', function() {
+      view.willDestroyElement();
+      expect(view.get('controller.isPolling')).to.be.false;
+    });
+  });
+
+  describe('#updateFilter()', function() {
+    beforeEach(function () {
+      sinon.stub(view, 'saveFilterConditions', Em.K);
+      sinon.stub(view, 'refresh', Em.K);
+      sinon.spy(view, 'updateFilter');
+    });
+    afterEach(function () {
+      view.saveFilterConditions.restore();
+      view.updateFilter.restore();
+      view.refresh.restore();
+    });
+    it('filteringComplete is false', function() {
+      this.clock = sinon.useFakeTimers();
+
+      view.set('filteringComplete', false);
+      view.updateFilter(1, '', 'string');
+      expect(view.get('controller.resetStartIndex')).to.be.false;
+      expect(view.saveFilterConditions.calledWith(1, '', 'string', false)).to.be.true;
+      view.set('filteringComplete', true);
+      this.clock.tick(view.get('filterWaitingTime'));
+      expect(view.updateFilter.calledWith(1, '', 'string')).to.be.true;
+      this.clock.restore();
+    });
+    it('filteringComplete is true', function() {
+      view.set('filteringComplete', true);
+
+      view.updateFilter(1, '', 'string');
+      expect(view.get('controller.resetStartIndex')).to.be.true;
+      expect(view.refresh.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#refresh()', function() {
+    it('', function() {
+      sinon.spy(view.get('controller'), 'load');
+      view.refresh();
+      expect(view.get('filteringComplete')).to.be.false;
+      expect(view.get('controller').load.calledOnce).to.be.true;
+      view.get('controller').load.restore();
+    });
+  });
+
+  describe('#resetStartIndex()', function() {
+    it('resetStartIndex is false and filteredCount is 0', function() {
+      view.set('filteredCount', 0);
+      view.set('controller.resetStartIndex', false);
+      view.set('startIndex', 0);
+      view.resetStartIndex();
+      expect(view.get('startIndex')).to.equal(0);
+    });
+    it('resetStartIndex is true and filteredCount is 0', function() {
+      view.set('filteredCount', 0);
+      view.set('controller.resetStartIndex', true);
+      view.set('startIndex', 0);
+      view.resetStartIndex();
+      expect(view.get('startIndex')).to.equal(0);
+    });
+    it('resetStartIndex is false and filteredCount is 5', function() {
+      view.set('filteredCount', 5);
+      view.set('controller.resetStartIndex', false);
+      view.set('startIndex', 0);
+      view.resetStartIndex();
+      expect(view.get('startIndex')).to.equal(0);
+    });
+    it('resetStartIndex is true and filteredCount is 5', function() {
+      view.set('controller.resetStartIndex', true);
+      view.set('filteredCount', 5);
+      view.set('startIndex', 0);
+      view.resetStartIndex();
+      expect(view.get('startIndex')).to.equal(1);
+    });
+  });
+
+});