You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2016/09/14 18:40:32 UTC

ambari git commit: AMBARI-18388. Link UI: add missing unit tests for models/configs/* and models/stack_version/* files (alexantonenko)

Repository: ambari
Updated Branches:
  refs/heads/trunk 752e7c463 -> 08e426ac7


AMBARI-18388. Link UI: add missing unit tests for models/configs/* and models/stack_version/* files
 (alexantonenko)


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

Branch: refs/heads/trunk
Commit: 08e426ac790957e3fab3edeef128a331fd5860bc
Parents: 752e7c4
Author: Alex Antonenko <hi...@gmail.com>
Authored: Wed Sep 14 18:32:11 2016 +0300
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Wed Sep 14 21:40:26 2016 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   7 +-
 .../app/models/stack_version/service_simple.js  |   2 +-
 ambari-web/app/models/stack_version/version.js  |   2 +-
 ambari-web/test/aliases/computed/concat.js      |  92 ++++++++++
 ambari-web/test/aliases/computed/empty.js       | 125 ++++++++++++++
 ambari-web/test/aliases/computed/equal.js       |   6 +-
 ambari-web/test/init_computed_aliases.js        |  23 ++-
 ambari-web/test/models/configs/section_test.js  |  55 ------
 .../test/models/configs/sub_section_test.js     | 103 -----------
 .../test/models/configs/theme/section_test.js   |  55 ++++++
 .../configs/theme/sub_section_tab_test.js       | 169 +++++++++++++++++++
 .../models/configs/theme/sub_section_test.js    | 103 +++++++++++
 .../models/stack_version/service_simple_test.js | 119 +++++++++++++
 .../test/models/stack_version/version_test.js   |  38 +++++
 14 files changed, 734 insertions(+), 165 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/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 d918900..78c6196 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -401,16 +401,19 @@ var files = [
   'test/models/host_stack_version_test',
   'test/models/upgrade_entity_test',
   'test/models/finished_upgrade_entity_test',
-  'test/models/configs/sub_section_test',
-  'test/models/configs/section_test',
   'test/models/configs/service_config_version_test',
   'test/models/configs/config_group_test',
   'test/models/configs/stack_config_property_test',
   'test/models/configs/objects/service_config_test',
   'test/models/configs/objects/service_config_category_test',
   'test/models/configs/objects/service_config_property_test',
+  'test/models/configs/theme/section_test',
+  'test/models/configs/theme/sub_section_test',
+  'test/models/configs/theme/sub_section_tab_test',
   'test/models/configs/theme/tab_test',
   'test/models/stack_version/repository_version_test',
+  'test/models/stack_version/service_simple_test',
+  'test/models/stack_version/version_test',
   'test/routes/views_test',
   //contains test with fake timers that affect Date
   'test/utils/lazy_loading_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/app/models/stack_version/service_simple.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/stack_version/service_simple.js b/ambari-web/app/models/stack_version/service_simple.js
index f693cc8..c7d94e5 100644
--- a/ambari-web/app/models/stack_version/service_simple.js
+++ b/ambari-web/app/models/stack_version/service_simple.js
@@ -30,7 +30,7 @@ App.ServiceSimple = DS.Model.extend({
 
   doNotShowAndInstall: function () {
     var skipServices = ['KERBEROS'];
-    if(!App.supports.installGanglia) {
+    if(!App.get('supports.installGanglia')) {
       skipServices.push('GANGLIA');
     }
     return skipServices.contains(this.get('name'));

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/app/models/stack_version/version.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/stack_version/version.js b/ambari-web/app/models/stack_version/version.js
index 3f2fe37..4e18ca4 100644
--- a/ambari-web/app/models/stack_version/version.js
+++ b/ambari-web/app/models/stack_version/version.js
@@ -35,7 +35,7 @@ App.StackVersion = DS.Model.extend({
   upgradeFailedHosts: DS.attr('array'),
   currentHosts: DS.attr('array'),
 
-  noInstalledHosts:  Em.computed.empty('installedHosts'),
+  noInstalledHosts: Em.computed.empty('installedHosts'),
 
   noCurrentHosts: Em.computed.empty('currentHosts'),
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/aliases/computed/concat.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/aliases/computed/concat.js b/ambari-web/test/aliases/computed/concat.js
new file mode 100644
index 0000000..846b42a
--- /dev/null
+++ b/ambari-web/test/aliases/computed/concat.js
@@ -0,0 +1,92 @@
+/**
+ * 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 helpers = App.TestAliases.helpers;
+
+App.TestAliases.testAsComputedConcat = function (context, propertyName, separator, dependentValues) {
+
+  describe('#' + propertyName + ' as Em.computed.concat', function () {
+
+    var cases = [
+        {
+          firstItem: undefined
+        },
+        {
+          firstItem: null
+        },
+        {
+          firstItem: '',
+          description: 'empty string'
+        },
+        {
+          firstItem: 0,
+          description: 'number'
+        },
+        {
+          firstItem: [1, 2],
+          description: 'array'
+        },
+        {
+          firstItem: {
+            property: 'value'
+          },
+          description: 'object'
+        },
+        {
+          firstItem: NaN
+        },
+        {
+          firstItem: Em.K,
+          description: 'function'
+        }
+      ],
+      values = dependentValues.map(function (key, index) {
+        return index.toString();
+      }),
+      valuesHash = {};
+
+    dependentValues.forEach(function (key, index) {
+      valuesHash[key] = index;
+    });
+
+    afterEach(function () {
+      helpers.smartRestoreGet(context);
+    });
+
+    it('has valid dependent keys', function () {
+      expect(Em.meta(context).descs[propertyName]._dependentKeys).to.eql(dependentValues);
+    });
+
+    cases.forEach(function (item) {
+
+      var description = item.description || item.firstItem;
+
+      it('values contain ' + description, function () {
+        if (item.hasOwnProperty('firstItem')) {
+          values[0] = item.firstItem;
+          valuesHash[dependentValues[0]] = item.firstItem;
+        }
+        helpers.smartStubGet(context, valuesHash).propertyDidChange(context, propertyName);
+        expect(helpers.smartGet(context, propertyName)).to.equal(values.join(separator));
+      });
+
+    });
+
+  });
+
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/aliases/computed/empty.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/aliases/computed/empty.js b/ambari-web/test/aliases/computed/empty.js
new file mode 100644
index 0000000..c054963
--- /dev/null
+++ b/ambari-web/test/aliases/computed/empty.js
@@ -0,0 +1,125 @@
+/**
+ * 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 helpers = App.TestAliases.helpers;
+
+App.TestAliases.testAsComputedEmpty = function (context, propertyName, dependentKey) {
+
+  describe('#' + propertyName + ' as Em.computed.empty', function () {
+
+    var values = [
+        {
+          value: undefined,
+          result: true
+        },
+        {
+          value: null,
+          result: true
+        },
+        {
+          value: '',
+          result: true,
+          description: 'empty string'
+        },
+        {
+          value: [],
+          result: true,
+          description: 'empty array'
+        },
+        {
+          value: false,
+          result: false
+        },
+        {
+          value: true,
+          result: false
+        },
+        {
+          value: 0,
+          result: false
+        },
+        {
+          value: NaN,
+          result: false
+        },
+        {
+          value: {},
+          result: false,
+          description: 'empty object'
+        },
+        {
+          value: '0',
+          result: false,
+          description: 'non-empty string'
+        },
+        {
+          value: 1,
+          result: false,
+          description: 'non-zero number'
+        },
+        {
+          value: [0],
+          result: false,
+          description: 'non-empty array'
+        },
+        {
+          value: {
+            i: 0
+          },
+          result: false,
+          description: 'non-empty object'
+        },
+        {
+          value: Em.K,
+          result: false,
+          description: 'function'
+        }
+      ],
+      descs = Em.meta(context).descs;
+
+    it('has valid dependent keys', function () {
+      expect(descs[propertyName]._dependentKeys).to.eql([dependentKey]);
+    });
+
+    values.forEach(function (item) {
+
+      var description = item.description || item.value,
+        initialDefinition = Boolean(descs[dependentKey]) ? descs[dependentKey] : context.get(dependentKey);
+
+      describe(dependentKey + ' is ' + description, function () {
+
+        beforeEach(function () {
+          helpers.reopenProperty(context, dependentKey, item.value);
+          context.propertyDidChange(propertyName);
+        });
+
+        afterEach(function () {
+          helpers.reopenProperty(context, dependentKey, initialDefinition);
+        });
+
+        it(propertyName + ' should be `' + item.result + '`', function () {
+          expect(context.get(propertyName)).to.equal(item.result);
+        });
+
+      });
+
+    });
+
+  });
+
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/aliases/computed/equal.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/aliases/computed/equal.js b/ambari-web/test/aliases/computed/equal.js
index 00ec2d1..77616ea 100644
--- a/ambari-web/test/aliases/computed/equal.js
+++ b/ambari-web/test/aliases/computed/equal.js
@@ -45,7 +45,11 @@ App.TestAliases.testAsComputedEqual = function (context, propertyName, dependent
     });
 
     it('should be `false` if ' + JSON.stringify(dependentKey) + ' is not equal to the ' + JSON.stringify(neededValue), function () {
-      helpers.smartStubGet(context, dependentKey, Math.random())
+      var randomValue = Math.random();
+      if (randomValue === neededValue) {
+        randomValue++;
+      }
+      helpers.smartStubGet(context, dependentKey, randomValue)
         .propertyDidChange(context, propertyName);
       var value = helpers.smartGet(context, propertyName);
       expect(value).to.be.false;

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/init_computed_aliases.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/init_computed_aliases.js b/ambari-web/test/init_computed_aliases.js
index 44faf4f..81ae5fa 100644
--- a/ambari-web/test/init_computed_aliases.js
+++ b/ambari-web/test/init_computed_aliases.js
@@ -88,7 +88,7 @@ App.TestAliases = {
      * @returns {App.TestAliases}
      * @private
      */
-    _stubOneKey: function (self,dependentKey, value) {
+    _stubOneKey: function (self, dependentKey, value) {
       var isApp = dependentKey.startsWith('App.');
       var name = isApp ? dependentKey.replace('App.', '') : dependentKey;
       var context = isApp ? App : self;
@@ -160,6 +160,23 @@ App.TestAliases = {
         result.push(combo);
       }
       return result;
+    },
+
+    /**
+     * Reopens property of the given object as constant with the given value
+     * @param {Ember.Object} context
+     * @param {String} key
+     * @param {*} value
+     */
+    reopenProperty: function (context, key, value) {
+      var reopenObject = {},
+        isUndefined = typeof value === 'undefined';
+      // if the only property in reopen argument is undefined, context won't be changed
+      reopenObject[key] = isUndefined ? null : value;
+      context.reopen(reopenObject);
+      if (isUndefined) {
+        context.set(key, undefined);
+      }
     }
 
   }
@@ -199,4 +216,6 @@ require('test/aliases/computed/and');
 require('test/aliases/computed/or');
 require('test/aliases/computed/formatUnavailable');
 require('test/aliases/computed/getByKey');
-require('test/aliases/computed/truncate');
\ No newline at end of file
+require('test/aliases/computed/truncate');
+require('test/aliases/computed/concat');
+require('test/aliases/computed/empty');
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/models/configs/section_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/configs/section_test.js b/ambari-web/test/models/configs/section_test.js
deleted file mode 100644
index 3c9ab40..0000000
--- a/ambari-web/test/models/configs/section_test.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-var model;
-
-function getModel() {
-  return App.Section.createRecord();
-}
-
-describe('App.Section', function () {
-
-  beforeEach(function () {
-    model = getModel();
-  });
-
-  describe('#errorsCount', function () {
-
-    beforeEach(function () {
-      model.reopen({subSections: [
-        App.SubSection.createRecord({configs: [
-          App.ServiceConfigProperty.create({isValid: true}),
-          App.ServiceConfigProperty.create({isValid: false})
-        ]}),
-        App.SubSection.createRecord({configs: [
-          App.ServiceConfigProperty.create({isValid: true}),
-          App.ServiceConfigProperty.create({isValid: false})
-        ]})
-      ]});
-    });
-
-    it('should use subsections.@each.errorsCount', function () {
-      expect(model.get('errorsCount')).to.equal(2);
-    });
-
-  });
-
-  App.TestAliases.testAsComputedEveryBy(getModel(), 'isHiddenByFilter', 'subSections', 'isSectionVisible', false);
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/models/configs/sub_section_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/configs/sub_section_test.js b/ambari-web/test/models/configs/sub_section_test.js
deleted file mode 100644
index e89bce9..0000000
--- a/ambari-web/test/models/configs/sub_section_test.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-var model;
-
-function getModel() {
-  return App.SubSection.createRecord();
-}
-
-describe('App.SubSection', function () {
-
-  beforeEach(function () {
-    model = getModel();
-  });
-
-  App.TestAliases.testAsComputedAnd(getModel(), 'showTabs', ['hasTabs', 'someSubSectionTabIsVisible']);
-
-  App.TestAliases.testAsComputedAnd(getModel(), 'addLeftVerticalSplitter', ['!isFirstColumn', 'leftVerticalSplitter']);
-
-  App.TestAliases.testAsComputedAnd(getModel(), 'showTopSplitter', ['!isFirstRow', '!border']);
-
-  App.TestAliases.testAsComputedAnd(getModel(), 'isSectionVisible', ['!isHiddenByFilter', '!isHiddenByConfig', 'someConfigIsVisible']);
-
-  describe('#errorsCount', function () {
-
-    beforeEach(function () {
-      model.set('configs', [
-        App.ServiceConfigProperty.create({isValid: true}),
-        App.ServiceConfigProperty.create({isValid: false}),
-        App.ServiceConfigProperty.create({isValid: false}),
-        App.ServiceConfigProperty.create({isValid: false}),
-      ]);
-    });
-
-    it('should use configs.@each.isValid', function () {
-      expect(model.get('errorsCount')).to.equal(3);
-    });
-
-    it('should use configs.@each.isValidOverride', function() {
-      // original value is valid
-      var validOriginalSCP = model.get('configs').objectAt(0);
-      // add override with not valid value
-      validOriginalSCP.set('isValidOverride', false);
-      validOriginalSCP.set('isValid', true);
-      expect(model.get('errorsCount')).to.equal(3);
-    });
-
-  });
-
-  describe('#isHiddenByFilter', function () {
-
-    Em.A([
-        {
-          configs: [],
-          e: false,
-          m: 'Can\'t be hidden if there is no configs'
-        },
-        {
-          configs: [Em.Object.create({isHiddenByFilter: true, isVisible: true}), Em.Object.create({isHiddenByFilter: true, isVisible: true})],
-          e: true,
-          m: 'All configs are hidden'
-        },
-        {
-          configs: [Em.Object.create({isHiddenByFilter: false, isVisible: true}), Em.Object.create({isHiddenByFilter: true, isVisible: true})],
-          e: false,
-          m: 'Some configs are hidden'
-        },
-        {
-          configs: [Em.Object.create({isHiddenByFilter: false, isVisible: true}), Em.Object.create({isHiddenByFilter: true, isVisible: true})],
-          e: false,
-          m: 'Some configs are hidden'
-        },
-        {
-          configs: [Em.Object.create({isHiddenByFilter: false, isVisible: true}), Em.Object.create({isHiddenByFilter: false, isVisible: true})],
-          e: false,
-          m: 'No configs are hidden'
-        }
-    ]).forEach(function (test) {
-        it(test.m, function () {
-          model.set('configs', test.configs);
-          expect(model.get('isHiddenByFilter')).to.equal(test.e);
-        })
-      });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/models/configs/theme/section_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/configs/theme/section_test.js b/ambari-web/test/models/configs/theme/section_test.js
new file mode 100644
index 0000000..3c9ab40
--- /dev/null
+++ b/ambari-web/test/models/configs/theme/section_test.js
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+var model;
+
+function getModel() {
+  return App.Section.createRecord();
+}
+
+describe('App.Section', function () {
+
+  beforeEach(function () {
+    model = getModel();
+  });
+
+  describe('#errorsCount', function () {
+
+    beforeEach(function () {
+      model.reopen({subSections: [
+        App.SubSection.createRecord({configs: [
+          App.ServiceConfigProperty.create({isValid: true}),
+          App.ServiceConfigProperty.create({isValid: false})
+        ]}),
+        App.SubSection.createRecord({configs: [
+          App.ServiceConfigProperty.create({isValid: true}),
+          App.ServiceConfigProperty.create({isValid: false})
+        ]})
+      ]});
+    });
+
+    it('should use subsections.@each.errorsCount', function () {
+      expect(model.get('errorsCount')).to.equal(2);
+    });
+
+  });
+
+  App.TestAliases.testAsComputedEveryBy(getModel(), 'isHiddenByFilter', 'subSections', 'isSectionVisible', false);
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/models/configs/theme/sub_section_tab_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/configs/theme/sub_section_tab_test.js b/ambari-web/test/models/configs/theme/sub_section_tab_test.js
new file mode 100644
index 0000000..6044432
--- /dev/null
+++ b/ambari-web/test/models/configs/theme/sub_section_tab_test.js
@@ -0,0 +1,169 @@
+/**
+ * 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('models/configs/theme/sub_section_tab');
+
+describe('App.SubSectionTab', function () {
+
+  var subSectionTab,
+    getModel = function () {
+      return App.SubSectionTab.createRecord();
+    };
+
+  beforeEach(function () {
+    subSectionTab = getModel();
+  });
+
+  describe('#visibleProperties', function () {
+
+    var configs = [
+      Em.Object.create({
+        id: 0,
+        isVisible: false,
+        hiddenBySection: false
+      }),
+      Em.Object.create({
+        id: 1,
+        isVisible: false,
+        hiddenBySection: true
+      }),
+      Em.Object.create({
+        id: 2,
+        isVisible: true,
+        hiddenBySection: false
+      }),
+      Em.Object.create({
+        id: 3,
+        isVisible: true,
+        hiddenBySection: true
+      }),
+      Em.Object.create({
+        id: 4,
+        isVisible: false
+      }),
+      Em.Object.create({
+        id: 5,
+        isVisible: true
+      })
+    ];
+
+    it('should include visible properties from visible sections only', function () {
+      subSectionTab.set('configs', configs);
+      expect(subSectionTab.get('visibleProperties').mapProperty('id')).to.eql([2, 5]);
+    });
+
+  });
+
+  describe('#errorsCount', function () {
+
+    var configs = [
+      Em.Object.create({
+        isVisible: true,
+        isValid: true,
+        isValidOverride: true
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValid: true,
+        isValidOverride: true
+      }),
+      Em.Object.create({
+        isVisible: true,
+        isValid: true,
+        isValidOverride: false
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValid: true,
+        isValidOverride: false
+      }),
+      Em.Object.create({
+        isVisible: true,
+        isValid: false,
+        isValidOverride: true
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValid: false,
+        isValidOverride: true
+      }),
+      Em.Object.create({
+        isVisible: true,
+        isValid: false,
+        isValidOverride: false
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValid: false,
+        isValidOverride: false
+      }),
+      Em.Object.create({
+        isVisible: true,
+        isValid: true
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValid: true
+      }),
+      Em.Object.create({
+        isVisible: true,
+        isValid: false
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValid: false
+      }),
+      Em.Object.create({
+        isVisible: true,
+        isValidOverride: true
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValidOverride: true
+      }),
+      Em.Object.create({
+        isVisible: true,
+        isValidOverride: false
+      }),
+      Em.Object.create({
+        isVisible: false,
+        isValidOverride: false
+      }),
+      Em.Object.create({
+        isVisible: true
+      }),
+      Em.Object.create({
+        isVisible: false
+      })
+    ];
+
+    it('should include visible properties with errors', function () {
+      subSectionTab.set('configs', configs);
+      expect(subSectionTab.get('errorsCount')).to.eql(8);
+    });
+
+  });
+
+  App.TestAliases.testAsComputedEveryBy(getModel(), 'isHiddenByFilter', 'visibleProperties', 'isHiddenByFilter', true);
+
+  App.TestAliases.testAsComputedGt(getModel(), 'someConfigIsVisible', 'visibleProperties.length', 0);
+
+  App.TestAliases.testAsComputedAnd(getModel(), 'isVisible', ['!isHiddenByFilter', '!isHiddenByConfig', 'someConfigIsVisible']);
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/models/configs/theme/sub_section_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/configs/theme/sub_section_test.js b/ambari-web/test/models/configs/theme/sub_section_test.js
new file mode 100644
index 0000000..e89bce9
--- /dev/null
+++ b/ambari-web/test/models/configs/theme/sub_section_test.js
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+var model;
+
+function getModel() {
+  return App.SubSection.createRecord();
+}
+
+describe('App.SubSection', function () {
+
+  beforeEach(function () {
+    model = getModel();
+  });
+
+  App.TestAliases.testAsComputedAnd(getModel(), 'showTabs', ['hasTabs', 'someSubSectionTabIsVisible']);
+
+  App.TestAliases.testAsComputedAnd(getModel(), 'addLeftVerticalSplitter', ['!isFirstColumn', 'leftVerticalSplitter']);
+
+  App.TestAliases.testAsComputedAnd(getModel(), 'showTopSplitter', ['!isFirstRow', '!border']);
+
+  App.TestAliases.testAsComputedAnd(getModel(), 'isSectionVisible', ['!isHiddenByFilter', '!isHiddenByConfig', 'someConfigIsVisible']);
+
+  describe('#errorsCount', function () {
+
+    beforeEach(function () {
+      model.set('configs', [
+        App.ServiceConfigProperty.create({isValid: true}),
+        App.ServiceConfigProperty.create({isValid: false}),
+        App.ServiceConfigProperty.create({isValid: false}),
+        App.ServiceConfigProperty.create({isValid: false}),
+      ]);
+    });
+
+    it('should use configs.@each.isValid', function () {
+      expect(model.get('errorsCount')).to.equal(3);
+    });
+
+    it('should use configs.@each.isValidOverride', function() {
+      // original value is valid
+      var validOriginalSCP = model.get('configs').objectAt(0);
+      // add override with not valid value
+      validOriginalSCP.set('isValidOverride', false);
+      validOriginalSCP.set('isValid', true);
+      expect(model.get('errorsCount')).to.equal(3);
+    });
+
+  });
+
+  describe('#isHiddenByFilter', function () {
+
+    Em.A([
+        {
+          configs: [],
+          e: false,
+          m: 'Can\'t be hidden if there is no configs'
+        },
+        {
+          configs: [Em.Object.create({isHiddenByFilter: true, isVisible: true}), Em.Object.create({isHiddenByFilter: true, isVisible: true})],
+          e: true,
+          m: 'All configs are hidden'
+        },
+        {
+          configs: [Em.Object.create({isHiddenByFilter: false, isVisible: true}), Em.Object.create({isHiddenByFilter: true, isVisible: true})],
+          e: false,
+          m: 'Some configs are hidden'
+        },
+        {
+          configs: [Em.Object.create({isHiddenByFilter: false, isVisible: true}), Em.Object.create({isHiddenByFilter: true, isVisible: true})],
+          e: false,
+          m: 'Some configs are hidden'
+        },
+        {
+          configs: [Em.Object.create({isHiddenByFilter: false, isVisible: true}), Em.Object.create({isHiddenByFilter: false, isVisible: true})],
+          e: false,
+          m: 'No configs are hidden'
+        }
+    ]).forEach(function (test) {
+        it(test.m, function () {
+          model.set('configs', test.configs);
+          expect(model.get('isHiddenByFilter')).to.equal(test.e);
+        })
+      });
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/models/stack_version/service_simple_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/stack_version/service_simple_test.js b/ambari-web/test/models/stack_version/service_simple_test.js
new file mode 100644
index 0000000..ae00f9c
--- /dev/null
+++ b/ambari-web/test/models/stack_version/service_simple_test.js
@@ -0,0 +1,119 @@
+/**
+ * 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('models/stack_version/service_simple');
+
+describe('App.ServiceSimple', function () {
+
+  var serviceSimple;
+
+  beforeEach(function () {
+    serviceSimple = App.ServiceSimple.createRecord();
+  });
+
+  describe('#isHidden', function () {
+
+    var cases = [
+      {
+        name: 'MAPREDUCE2',
+        doNotShowAndInstall: false,
+        isHidden: true,
+        title: 'MapReduce2 isn\'t displayed in wizard as separate service'
+      },
+      {
+        name: 'KERBEROS',
+        doNotShowAndInstall: true,
+        isHidden: true,
+        title: 'Kerberos isn\'t displayed in wizard'
+      },
+      {
+        name: 'ZOOKEEPER',
+        doNotShowAndInstall: false,
+        isHidden: false,
+        title: 'ZooKeeper is displayed in wizard'
+      }
+    ];
+
+    cases.forEach(function (item) {
+
+      it(item.title, function () {
+        serviceSimple.reopen({
+          doNotShowAndInstall: item.doNotShowAndInstall
+        });
+        serviceSimple.set('name', item.name);
+        expect(serviceSimple.get('isHidden')).to.equal(item.isHidden);
+      });
+
+    });
+
+  });
+
+  describe('#doNotShowAndInstall', function () {
+
+    var cases = [
+      {
+        name: 'KERBEROS',
+        isGangliaSupported: false,
+        doNotShowAndInstall: true,
+        title: 'Kerberos can\'t be installed from wizard'
+      },
+      {
+        name: 'GANGLIA',
+        isGangliaSupported: false,
+        doNotShowAndInstall: true,
+        title: 'Ganglia can\'t be installed from wizard by default'
+      },
+      {
+        name: 'GANGLIA',
+        isGangliaSupported: true,
+        doNotShowAndInstall: false,
+        title: 'Ganglia can be installed from wizard only if experimental feature is enabled'
+      },
+      {
+        name: 'ZOOKEEPER',
+        isGangliaSupported: false,
+        doNotShowAndInstall: false,
+        title: 'ZooKeeper can be installed from wizard'
+      }
+    ];
+
+    cases.forEach(function (item) {
+
+      describe(item.name, function () {
+
+        beforeEach(function () {
+          sinon.stub(App, 'get').withArgs('supports.installGanglia').returns(item.isGangliaSupported);
+          serviceSimple.set('name', item.name);
+        });
+
+        afterEach(function () {
+          App.get.restore();
+        });
+
+        it(item.title, function () {
+          expect(serviceSimple.get('doNotShowAndInstall')).to.equal(item.doNotShowAndInstall);
+        });
+
+      });
+
+    });
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/08e426ac/ambari-web/test/models/stack_version/version_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/stack_version/version_test.js b/ambari-web/test/models/stack_version/version_test.js
new file mode 100644
index 0000000..d097bbd
--- /dev/null
+++ b/ambari-web/test/models/stack_version/version_test.js
@@ -0,0 +1,38 @@
+/**
+ * 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('models/stack_version/version');
+
+describe('App.StackVersion', function () {
+
+  var getModel = function () {
+    return App.StackVersion.createRecord();
+  };
+
+  App.TestAliases.testAsComputedConcat(getModel(), 'name', ' ', ['stack', 'version']);
+
+  App.TestAliases.testAsComputedEmpty(getModel(), 'noInstalledHosts', 'installedHosts');
+
+  App.TestAliases.testAsComputedEmpty(getModel(), 'noCurrentHosts', 'currentHosts');
+
+  App.TestAliases.testAsComputedEmpty(getModel(), 'noInitHosts', 'notInstalledHosts');
+
+  App.TestAliases.testAsComputedEqual(getModel(), 'isCurrent', 'state', 'CURRENT');
+
+});
\ No newline at end of file