Jelajahi Sumber

AMBARI-5347 Add UI unit tests (atkach)

atkach 11 tahun lalu
induk
melakukan
994631c3b9

+ 5 - 0
ambari-web/app/assets/test/tests.js

@@ -85,6 +85,7 @@ require('test/views/common/chart/linear_time_test');
 require('test/views/common/filter_view_test');
 require('test/views/common/table_view_test');
 require('test/views/common/quick_link_view_test');
+require('test/views/common/rolling_restart_view_test');
 require('test/views/main/dashboard_test');
 require('test/views/main/dashboard/widget_test');
 require('test/views/main/dashboard/widgets/text_widget_test');
@@ -107,7 +108,11 @@ require('test/views/main/host/summary_test');
 require('test/views/main/host/details/host_component_view_test');
 require('test/views/main/host/details/host_component_views/decommissionable_test');
 require('test/views/main/jobs/hive_job_details_tez_dag_view_test');
+require('test/views/main/charts/heatmap/heatmap_host_test');
+require('test/views/main/charts/heatmap/heatmap_rack_test');
+require('test/views/main/service/info/config_test');
 require('test/views/common/configs/services_config_test');
+require('test/views/wizard/step1_view_test');
 require('test/views/wizard/step3_view_test');
 require('test/views/wizard/step9_view_test');
 require('test/models/host_test');

+ 5 - 5
ambari-web/app/views/common/configs/services_config.js

@@ -66,15 +66,15 @@ App.ServiceConfigView = Em.View.extend({
    * Check if we should show Custom Property category
    */
   checkCanEdit: function () {
-    var controller = App.get('router.'+this.get('controller.name'));
-    if(!this.get('controller.selectedService.configCategories')){
+    var controller = this.get('controller');
+    if (!controller.get('selectedService.configCategories')) {
       return;
     }
     var canAddProperty = true;
-    if(controller.get('selectedConfigGroup') && !controller.get('selectedConfigGroup').isDefault){
-     canAddProperty = false;
+    if (controller.get('selectedConfigGroup') && !controller.get('selectedConfigGroup').isDefault) {
+      canAddProperty = false;
     }
-    this.get('controller.selectedService.configCategories').filterProperty('siteFileName').forEach(function (config) {
+    controller.get('selectedService.configCategories').filterProperty('siteFileName').forEach(function (config) {
       if (config.get('canAddProperty') !== false) {
         config.set('canAddProperty', canAddProperty);
       }

+ 1 - 1
ambari-web/app/views/common/rolling_restart_view.js

@@ -85,7 +85,7 @@ App.RollingRestartView = Em.View.extend({
    */
   initialize : function() {
     if (this.get('batchSize') == -1 && this.get('interBatchWaitTimeSeconds') == -1 && this.get('tolerateSize') == -1) {
-      var restartCount = this.get('restartHostComponents');
+      var restartCount = this.get('restartHostComponents.length');
       var batchSize = 1;
       if (restartCount > 10) {
         batchSize = Math.ceil(restartCount / 10);

+ 1 - 1
ambari-web/app/views/main/charts/heatmap/heatmap_rack.js

@@ -57,7 +57,7 @@ App.MainChartsHeatmapRackView = Em.View.extend({
     var rack = this.get('rack');
     var widthPercent = 100;
     var hostCount = rack.get('hosts.length');
-    if (hostCount < 11) {
+    if (hostCount && hostCount < 11) {
       widthPercent = (100 / hostCount) - 0.5;
     } else {
       widthPercent = 10; // max out at 10%

+ 153 - 0
ambari-web/test/views/common/configs/services_config_test.js

@@ -21,6 +21,79 @@ require('views/common/chart/pie');
 require('views/common/configs/services_config');
 
 
+describe('App.ServiceConfigView', function () {
+  var controller = App.WizardStep7Controller.create({
+    selectedServiceObserver: function(){},
+    switchConfigGroupConfigs: function(){}
+  });
+  var view = App.ServiceConfigView.create({
+    controller: controller
+  });
+  var testCases = [
+    {
+      title: 'selectedConfigGroup is null',
+      result: {
+        'category1': false,
+        'category2': true,
+        'category3': false
+      },
+      selectedConfigGroup: null,
+      selectedService: {
+        serviceName: 'TEST',
+        configCategories: [
+          App.ServiceConfigCategory.create({ name: 'category1', canAddProperty: false}),
+          App.ServiceConfigCategory.create({ name: 'category2', siteFileName: 'xml', canAddProperty: true}),
+          App.ServiceConfigCategory.create({ name: 'category3', siteFileName: 'xml', canAddProperty: false})
+        ]
+      }
+    },
+    {
+      title: 'selectedConfigGroup is default group',
+      result: {
+        'category1': true,
+        'category2': true,
+        'category3': false
+      },
+      selectedConfigGroup: {isDefault: true},
+      selectedService: {
+        serviceName: 'TEST',
+        configCategories: [
+          App.ServiceConfigCategory.create({ name: 'category1', canAddProperty: true}),
+          App.ServiceConfigCategory.create({ name: 'category2', siteFileName: 'xml', canAddProperty: true}),
+          App.ServiceConfigCategory.create({ name: 'category3', siteFileName: 'xml', canAddProperty: false})
+        ]
+      }
+    },
+    {
+      title: 'selectedConfigGroup is not default group',
+      result: {
+        'category1': false,
+        'category2': false
+      },
+      selectedConfigGroup: {},
+      selectedService: {
+        serviceName: 'TEST',
+        configCategories: [
+          App.ServiceConfigCategory.create({ name: 'category1', siteFileName: 'xml', canAddProperty: true}),
+          App.ServiceConfigCategory.create({ name: 'category2', siteFileName: 'xml', canAddProperty: false})
+        ]
+      }
+    }
+  ];
+  describe('#checkCanEdit', function () {
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        controller.set('selectedService', test.selectedService);
+        controller.set('selectedConfigGroup', test.selectedConfigGroup);
+        view.checkCanEdit();
+        controller.get('selectedService.configCategories').forEach(function (category) {
+          expect(category.get('canAddProperty')).to.equal(test.result[category.get('name')]);
+        });
+      });
+    });
+  });
+});
+
 describe('App.ServiceConfigsByCategoryView', function () {
 
   var view = App.ServiceConfigsByCategoryView.create({
@@ -101,6 +174,86 @@ describe('App.ServiceConfigsByCategoryView', function () {
         expect(view.sortByIndex(_test.configs).mapProperty('resultId')).to.deep.equal(result);
       })
     })
+  });
 
+  describe('#updateReadOnlyFlags', function () {
+    it('if canEdit is true then isEditable flag of configs shouldn\'t be changed', function () {
+      view.set('canEdit', true);
+      view.set('serviceConfigs', [
+        Em.Object.create({
+          name: 'config1',
+          isEditable: true
+        }),
+        Em.Object.create({
+          name: 'config2',
+          isEditable: false
+        })
+      ]);
+      view.updateReadOnlyFlags();
+      expect(view.get('serviceConfigs').findProperty('name', 'config1').get('isEditable')).to.equal(true);
+      expect(view.get('serviceConfigs').findProperty('name', 'config2').get('isEditable')).to.equal(false);
+    });
+    it('if canEdit is false then configs shouldn\'t be editable', function () {
+      view.set('canEdit', false);
+      view.set('serviceConfigs', [
+        Em.Object.create({
+          name: 'config1',
+          isEditable: true
+        }),
+        Em.Object.create({
+          name: 'config2',
+          isEditable: false
+        })
+      ]);
+      view.updateReadOnlyFlags();
+      expect(view.get('serviceConfigs').findProperty('name', 'config1').get('isEditable')).to.equal(false);
+      expect(view.get('serviceConfigs').findProperty('name', 'config2').get('isEditable')).to.equal(false);
+    });
+    it('if canEdit is false then config overrides shouldn\'t be editable', function () {
+      view.set('canEdit', false);
+      view.set('serviceConfigs', [
+        Em.Object.create({
+          name: 'config',
+          isEditable: true,
+          overrides: [
+            Em.Object.create({
+              name: 'override1',
+              isEditable: true
+            }),
+            Em.Object.create({
+              name: 'override2',
+              isEditable: false
+            })
+          ]
+        })
+      ]);
+      view.updateReadOnlyFlags();
+      var overrides = view.get('serviceConfigs').findProperty('name', 'config').get('overrides');
+      expect(overrides.findProperty('name', 'override1').get('isEditable')).to.equal(false);
+      expect(overrides.findProperty('name', 'override2').get('isEditable')).to.equal(false);
+    });
+    it('if canEdit is true then isEditable flag of overrides shouldn\'t be changed', function () {
+      view.set('canEdit', true);
+      view.set('serviceConfigs', [
+        Em.Object.create({
+          name: 'config',
+          isEditable: true,
+          overrides: [
+            Em.Object.create({
+              name: 'override1',
+              isEditable: true
+            }),
+            Em.Object.create({
+              name: 'override2',
+              isEditable: false
+            })
+          ]
+        })
+      ]);
+      view.updateReadOnlyFlags();
+      var overrides = view.get('serviceConfigs').findProperty('name', 'config').get('overrides');
+      expect(overrides.findProperty('name', 'override1').get('isEditable')).to.equal(true);
+      expect(overrides.findProperty('name', 'override2').get('isEditable')).to.equal(false);
+    })
   })
 });

+ 72 - 0
ambari-web/test/views/common/rolling_restart_view_test.js

@@ -0,0 +1,72 @@
+/**
+ * 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/rolling_restart_view');
+
+describe('App.RollingRestartView', function () {
+
+  var view = App.RollingRestartView.create({
+    restartHostComponents: []
+  });
+
+  describe('#initialize', function () {
+    var testCases = [
+      {
+        restartHostComponents: [],
+        result: {
+          batchSize: 1,
+          tolerateSize: 1
+        }
+      },
+      {
+        restartHostComponents: new Array(10),
+        result: {
+          batchSize: 1,
+          tolerateSize: 1
+        }
+      },
+      {
+        restartHostComponents: new Array(11),
+        result: {
+          batchSize: 2,
+          tolerateSize: 2
+        }
+      },
+      {
+        restartHostComponents: new Array(20),
+        result: {
+          batchSize: 2,
+          tolerateSize: 2
+        }
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.restartHostComponents.length + ' components to restart', function () {
+        view.set('batchSize', -1);
+        view.set('interBatchWaitTimeSeconds', -1);
+        view.set('tolerateSize', -1);
+        view.set('restartHostComponents', test.restartHostComponents);
+        view.initialize();
+        expect(view.get('batchSize')).to.equal(test.result.batchSize);
+        expect(view.get('tolerateSize')).to.equal(test.result.tolerateSize);
+      })
+    }, this);
+  });
+});

+ 120 - 0
ambari-web/test/views/main/charts/heatmap/heatmap_host_test.js

@@ -0,0 +1,120 @@
+/**
+ * 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/charts/heatmap/heatmap_host');
+
+describe('App.MainChartsHeatmapHostView', function() {
+
+  var view = App.MainChartsHeatmapHostView.create({
+    templateName: '',
+    controller: Em.Object.create(),
+    content: {}
+  });
+
+  describe('#hostTemperatureStyle', function () {
+    var testCases = [
+      {
+        title: 'if hostToSlotMap is null then hostTemperatureStyle should be empty',
+        hostName: 'host',
+        controller: Em.Object.create({
+          hostToSlotMap: null,
+          selectedMetric: {
+            slotDefinitions: []
+          }
+        }),
+        result: ''
+      },
+      {
+        title: 'if hostName is null then hostTemperatureStyle should be empty',
+        hostName: '',
+        controller: Em.Object.create({
+          hostToSlotMap: {},
+          selectedMetric: {
+            slotDefinitions: []
+          }
+        }),
+        result: ''
+      },
+      {
+        title: 'if slot less than 0 then hostTemperatureStyle should be empty',
+        hostName: 'host1',
+        controller: Em.Object.create({
+          hostToSlotMap: {
+            "host1": -1
+          },
+          selectedMetric: {
+            slotDefinitions: []
+          }
+        }),
+        result: ''
+      },
+      {
+        title: 'if slotDefinitions is null then hostTemperatureStyle should be empty',
+        hostName: 'host1',
+        controller: Em.Object.create({
+          hostToSlotMap: {
+            "host1": 1
+          },
+          selectedMetric: {
+            slotDefinitions: null
+          }
+        }),
+        result: ''
+      },
+      {
+        title: 'if slotDefinitions length not more than slot number then hostTemperatureStyle should be empty',
+        hostName: 'host1',
+        controller: Em.Object.create({
+          hostToSlotMap: {
+            "host1": 1
+          },
+          selectedMetric: {
+            slotDefinitions: [{}]
+          }
+        }),
+        result: ''
+      },
+      {
+        title: 'if slotDefinitions correct then hostTemperatureStyle should be "style1"',
+        hostName: 'host1',
+        controller: Em.Object.create({
+          hostToSlotMap: {
+            "host1": 1
+          },
+          selectedMetric: {
+            slotDefinitions: [
+              Em.Object.create({cssStyle: 'style0'}),
+              Em.Object.create({cssStyle: 'style1'})
+            ]
+          }
+        }),
+        result: 'style1'
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        view.set('content.hostName', test.hostName);
+        view.set('controller', test.controller);
+        expect(view.get('hostTemperatureStyle')).to.equal(test.result);
+      });
+    });
+  });
+
+});

+ 68 - 0
ambari-web/test/views/main/charts/heatmap/heatmap_rack_test.js

@@ -0,0 +1,68 @@
+/**
+ * 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/charts/heatmap/heatmap_rack');
+
+describe('App.MainChartsHeatmapRackView', function() {
+
+  var view = App.MainChartsHeatmapRackView.create({
+    templateName: ''
+  });
+
+  describe('#hostCssStyle', function () {
+    var testCases = [
+      {
+        title: 'if hosts number is zero then hostCssStyle should be have width 10%',
+        rack: Em.Object.create({
+          hosts: new Array(0)
+        }),
+        result: "width:10%;float:left;"
+      },
+      {
+        title: 'if hosts number is one then hostCssStyle should be have width 99.5%',
+        rack: Em.Object.create({
+          hosts: new Array(1)
+        }),
+        result: "width:99.5%;float:left;"
+      },
+      {
+        title: 'if hosts number is ten then hostCssStyle should be have width 9.5%',
+        rack: Em.Object.create({
+          hosts: new Array(10)
+        }),
+        result: "width:9.5%;float:left;"
+      },
+      {
+        title: 'if hosts number is ten then hostCssStyle should be have width 10%',
+        rack: Em.Object.create({
+          hosts: new Array(11)
+        }),
+        result: "width:10%;float:left;"
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        view.set('rack', test.rack);
+        expect(view.get('hostCssStyle')).to.equal(test.result);
+      });
+    });
+  });
+
+});

+ 76 - 0
ambari-web/test/views/main/service/info/config_test.js

@@ -0,0 +1,76 @@
+/**
+ * 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/service/info/configs');
+
+describe('App.MainServiceInfoConfigsView', function() {
+
+  var view = App.MainServiceInfoConfigsView.create({
+    controller: Em.Object.create()
+  });
+
+  describe('#updateComponentInformation', function() {
+
+    var testCases = [
+      {
+        title: 'if components absent then counters should be 0',
+        content: {
+          restartRequiredHostsAndComponents: {}
+        },
+        result: {
+          componentsCount: 0,
+          hostsCount: 0
+        }
+      },
+      {
+        title: 'if host doesn\'t have components then hostsCount should be 1 and componentsCount should be 0',
+        content: {
+          restartRequiredHostsAndComponents: {
+            host1: []
+          }
+        },
+        result: {
+          componentsCount: 0,
+          hostsCount: 1
+        }
+      },
+      {
+        title: 'if host has 1 component then hostsCount should be 1 and componentsCount should be 1',
+        content: {
+          restartRequiredHostsAndComponents: {
+            host1: [{}]
+          }
+        },
+        result: {
+          componentsCount: 1,
+          hostsCount: 1
+        }
+      }
+    ];
+    testCases.forEach(function(test) {
+      it(test.title, function() {
+        view.set('controller.content', test.content);
+        view.updateComponentInformation();
+        expect(view.get('componentsCount')).to.equal(test.result.componentsCount);
+        expect(view.get('hostsCount')).to.equal(test.result.hostsCount);
+      });
+    });
+  });
+});

+ 195 - 0
ambari-web/test/views/wizard/step1_view_test.js

@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+require('views/wizard/step1_view');
+
+describe('App.WizardStep1View', function () {
+  var view = App.WizardStep1View.create({
+    stacks: [],
+    updateByCheckbox: function () {
+    },
+    editGroupLocalRepository: function () {
+    },
+    controller: Em.Object.create(),
+    allRepoUnchecked: false
+  });
+
+  describe('#emptyRepoExist', function () {
+    it('none repos', function () {
+      view.set('allRepositoriesGroup', []);
+      expect(view.get('emptyRepoExist')).to.equal(false);
+    });
+    it('one not empty repo', function () {
+      view.set('allRepositoriesGroup', [
+        {
+          'empty-error': false
+        }
+      ]);
+      expect(view.get('emptyRepoExist')).to.equal(false);
+    });
+    it('one empty repo', function () {
+      view.set('allRepositoriesGroup', [
+        {
+          'empty-error': true
+        }
+      ]);
+      expect(view.get('emptyRepoExist')).to.equal(true);
+    });
+  });
+
+  describe('#invalidUrlExist', function () {
+    var invalidUrlExistTestCases = [
+      {
+        title: 'if invalid count more than 0 and validation failed then invalid URL should exist',
+        stacks: [
+          Em.Object.create({
+            isSelected: true,
+            invalidCnt: 1
+          })
+        ],
+        allRepositoriesGroup: [
+          {
+            'validation': 'icon-exclamation-sign'
+          }
+        ],
+        result: true
+      },
+      {
+        title: 'if invalid count equal 0 and validation failed then invalid URL shouldn\'t exist',
+        stacks: [
+          Em.Object.create({
+            isSelected: true,
+            invalidCnt: 0
+          })
+        ],
+        allRepositoriesGroup: [
+          {
+            'validation': 'icon-exclamation-sign'
+          }
+        ],
+        result: false
+      },
+      {
+        title: 'if invalid count more than 0 and validation passed then invalid URL shouldn\'t exist',
+        stacks: [
+          Em.Object.create({
+            isSelected: true,
+            invalidCnt: 1
+          })
+        ],
+        allRepositoriesGroup: [
+          {
+            'validation': 'icon-success'
+          }
+        ],
+        result: false
+      },
+      {
+        title: 'if invalid count equal 0 and validation passed then invalid URL shouldn\'t exist',
+        stacks: [
+          Em.Object.create({
+            isSelected: true,
+            invalidCnt: 0
+          })
+        ],
+        allRepositoriesGroup: [
+          {
+            'validation': 'icon-success'
+          }
+        ],
+        result: false
+      }
+    ];
+
+    invalidUrlExistTestCases.forEach(function (test) {
+      it(test.title, function () {
+        view.get('controller').set('content', {stacks: test.stacks});
+        view.set('allRepositoriesGroup', test.allRepositoriesGroup);
+        expect(view.get('invalidUrlExist')).to.equal(test.result);
+      });
+    });
+  });
+  describe('#totalErrorCnt', function () {
+    var totalErrorCntTestCases = [
+      {
+        title: 'if allRepoUnchecked is true then totalErrorCnt should be 1',
+        allRepoUnchecked: true,
+        allRepositoriesGroup: [
+          {
+            'empty-error': true,
+            'validation': 'icon-exclamation-sign'
+          }
+        ],
+        result: 1
+      },
+      {
+        title: 'if validation passed successfully then totalErrorCnt should be 0',
+        allRepoUnchecked: false,
+        allRepositoriesGroup: [
+          {
+            'empty-error': false,
+            'validation': 'icon-success'
+          }
+        ],
+        result: 0
+      },
+      {
+        title: 'if empty-error is true then totalErrorCnt should be 1',
+        allRepoUnchecked: false,
+        allRepositoriesGroup: [
+          {
+            'empty-error': true,
+            'validation': 'icon-success'
+          }
+        ],
+        result: 1
+      },
+      {
+        title: 'if validation failed then totalErrorCnt should be 1',
+        allRepoUnchecked: false,
+        allRepositoriesGroup: [
+          {
+            'empty-error': false,
+            'validation': 'icon-exclamation-sign'
+          }
+        ],
+        result: 1
+      },
+      {
+        title: 'if validation failed and empty-error is true then totalErrorCnt should be 2',
+        allRepoUnchecked: false,
+        allRepositoriesGroup: [
+          {
+            'empty-error': true,
+            'validation': 'icon-exclamation-sign'
+          }
+        ],
+        result: 2
+      }
+    ];
+
+    totalErrorCntTestCases.forEach(function (test) {
+      it(test.title, function () {
+        view.set('allRepoUnchecked', test.allRepoUnchecked);
+        view.set('allRepositoriesGroup', test.allRepositoriesGroup);
+        expect(view.get('totalErrorCnt')).to.equal(test.result);
+      });
+    });
+  });
+});