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 2013/10/18 14:05:32 UTC

git commit: AMBARI-3525 UI optimization: install wizard. (atkach)

Updated Branches:
  refs/heads/trunk aa4b88fed -> 5c767210d


AMBARI-3525 UI optimization: install wizard. (atkach)


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

Branch: refs/heads/trunk
Commit: 5c767210d83fd8bd07a984f59d43410c98ff73c6
Parents: aa4b88f
Author: atkach <an...@gmail.com>
Authored: Fri Oct 18 15:05:26 2013 +0300
Committer: atkach <an...@gmail.com>
Committed: Fri Oct 18 15:05:26 2013 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   1 +
 .../app/controllers/wizard/step3_controller.js  |  78 +++++++------
 .../app/controllers/wizard/step5_controller.js  |  19 +++-
 .../app/controllers/wizard/step6_controller.js  |  24 +++-
 ambari-web/app/routes/installer.js              |  12 +-
 ambari-web/app/utils/ajax.js                    |   1 -
 ambari-web/app/utils/lazy_loading.js            |  98 +++++++++++++++++
 ambari-web/app/views/wizard/step3_view.js       |   2 +-
 ambari-web/app/views/wizard/step5_view.js       |  21 ++++
 ambari-web/test/utils/lazy_loading_test.js      | 110 +++++++++++++++++++
 10 files changed, 319 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/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 014bf9f..521a0cc 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -70,6 +70,7 @@ require('test/utils/misc_test');
 require('test/utils/validator_test');
 require('test/utils/config_test');
 require('test/utils/string_utils_test');
+require('test/utils/lazy_loading_test');
 require('test/views/common/chart/linear_time_test');
 require('test/views/common/filter_view_test');
 require('test/views/common/quick_link_view_test');

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/controllers/wizard/step3_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step3_controller.js b/ambari-web/app/controllers/wizard/step3_controller.js
index ca55c9d..f123b9e 100644
--- a/ambari-web/app/controllers/wizard/step3_controller.js
+++ b/ambari-web/app/controllers/wizard/step3_controller.js
@@ -17,6 +17,7 @@
  */
 
 var App = require('app');
+var lazyloading = require('utils/lazy_loading');
 
 App.WizardStep3Controller = Em.Controller.extend({
   name: 'wizardStep3Controller',
@@ -99,28 +100,30 @@ App.WizardStep3Controller = Em.Controller.extend({
   }.property('hosts.@each.isChecked'),
 
   isRetryDisabled: true,
+  isLoaded: false,
 
   navigateStep: function () {
-    this.loadStep();
-    if (this.get('content.installOptions.manualInstall') !== true) {
-      if (!App.db.getBootStatus()) {
-        this.startBootstrap();
-      }
-    } else {
-      this.set('bootHosts', this.get('hosts'));
-      if (App.testMode) {
-        this.getHostInfo();
-        this.get('bootHosts').setEach('bootStatus', 'REGISTERED');
-        this.get('bootHosts').setEach('cpu', '2');
-        this.get('bootHosts').setEach('memory', '2000000');
-        this.set('isSubmitDisabled', false);
+    if(this.get('isLoaded')){
+      if (this.get('content.installOptions.manualInstall') !== true) {
+        if (!App.db.getBootStatus()) {
+          this.startBootstrap();
+        }
       } else {
-        this.set('registrationStartedAt', null);
-        this.get('bootHosts').setEach('bootStatus', 'DONE');
-        this.startRegistration();
+        this.set('bootHosts', this.get('hosts'));
+        if (App.testMode) {
+          this.getHostInfo();
+          this.get('bootHosts').setEach('bootStatus', 'REGISTERED');
+          this.get('bootHosts').setEach('cpu', '2');
+          this.get('bootHosts').setEach('memory', '2000000');
+          this.set('isSubmitDisabled', false);
+        } else {
+          this.set('registrationStartedAt', null);
+          this.get('bootHosts').setEach('bootStatus', 'DONE');
+          this.startRegistration();
+        }
       }
     }
-  },
+  }.observes('isLoaded'),
 
   clearStep: function () {
     this.set('stopBootstrap', false);
@@ -134,37 +137,42 @@ App.WizardStep3Controller = Em.Controller.extend({
   loadStep: function () {
     console.log("TRACE: Loading step3: Confirm Hosts");
     this.set('registrationStartedAt', null);
+    this.set('isLoaded', false);
 
     this.clearStep();
-    var hosts = this.loadHosts();
+    this.loadHosts();
     // hosts.setEach('bootStatus', 'RUNNING');
-    this.renderHosts(hosts);
   },
 
   /* Loads the hostinfo from localStorage on the insertion of view. It's being called from view */
   loadHosts: function () {
-    var hostInfo = this.get('content.hosts');
-    var hosts = new Ember.Set();
-    for (var index in hostInfo) {
-      hosts.add(hostInfo[index]);
-      console.log("TRACE: host name is: " + hostInfo[index].name);
-    }
-    return hosts;
-  },
+    var hostsInfo = this.get('content.hosts');
+    var hosts = [];
 
-  /* Renders the set of passed hosts */
-  renderHosts: function (hostsInfo) {
-    var self = this;
-    hostsInfo.forEach(function (_hostInfo) {
+    for (var index in hostsInfo) {
       var hostInfo = App.HostInfo.create({
-        name: _hostInfo.name,
-        bootStatus: _hostInfo.bootStatus,
+        name: hostsInfo[index].name,
+        bootStatus: hostsInfo[index].bootStatus,
         isChecked: false
       });
 
       console.log('pushing ' + hostInfo.name);
-      self.hosts.pushObject(hostInfo);
-    });
+      hosts.pushObject(hostInfo);
+    }
+
+    if(hosts.length > 100) {
+      lazyloading.run({
+        destination: this.get('hosts'),
+        source: hosts,
+        context: this,
+        initSize: 20,
+        chunkSize: 100,
+        delay: 300
+      });
+    } else {
+      this.set('hosts', hosts);
+      this.set('isLoaded', true);
+    }
   },
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/controllers/wizard/step5_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step5_controller.js b/ambari-web/app/controllers/wizard/step5_controller.js
index 2ee3187..c1ab556 100644
--- a/ambari-web/app/controllers/wizard/step5_controller.js
+++ b/ambari-web/app/controllers/wizard/step5_controller.js
@@ -56,6 +56,7 @@ App.WizardStep5Controller = Em.Controller.extend({
   }.property('servicesMasters.@each.selectedHost'),
 
   hosts:[],
+  isLazyLoading: false,
 
   servicesMasters:[],
   selectedServicesMasters:[],
@@ -220,7 +221,10 @@ App.WizardStep5Controller = Em.Controller.extend({
   renderComponents:function (masterComponents) {
     var services = this.get('content.services')
       .filterProperty('isInstalled', true).mapProperty('serviceName'); //list of shown services
-
+    var hosts = this.get('hosts');
+    //The lazy loading for select elements supported only by Firefox and Chrome
+    var isBrowserSupported = $.browser.mozilla || ($.browser.safari && navigator.userAgent.indexOf('Chrome') !== -1);
+    var isLazyLoading = isBrowserSupported && hosts.length > 100;
     var showRemoveControlZk = !services.contains('ZOOKEEPER') && masterComponents.filterProperty('display_name', 'ZooKeeper').length > 1;
     var showRemoveControlHb = !services.contains('HBASE') && masterComponents.filterProperty('component_name', 'HBASE_MASTER').length > 1;
     var zid = 1;
@@ -228,6 +232,8 @@ App.WizardStep5Controller = Em.Controller.extend({
     var nid = 1;
     var result = [];
 
+    this.set('isLazyLoading', isLazyLoading);
+
     masterComponents.forEach(function (item) {
 
       var componentObj = Ember.Object.create(item);
@@ -242,7 +248,16 @@ App.WizardStep5Controller = Em.Controller.extend({
       }  else if (item.component_name === "NAMENODE") {
         componentObj.set('zId', nid++);
       }
-      componentObj.set("availableHosts", this.get("hosts"));
+      if(isLazyLoading){
+        //select need at least 30 hosts to have scrollbar
+        var initialHosts = hosts.slice(0, 30);
+        if(!initialHosts.someProperty('host_name', item.selectedHost)){
+          initialHosts.push(hosts.findProperty('host_name', item.selectedHost));
+        }
+        componentObj.set("availableHosts", initialHosts);
+      } else {
+        componentObj.set("availableHosts", hosts);
+      }
       result.push(componentObj);
     }, this);
 

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/controllers/wizard/step6_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step6_controller.js b/ambari-web/app/controllers/wizard/step6_controller.js
index 5e0a411..4917cee 100644
--- a/ambari-web/app/controllers/wizard/step6_controller.js
+++ b/ambari-web/app/controllers/wizard/step6_controller.js
@@ -18,6 +18,7 @@
 
 var App = require('app');
 var db = require('utils/db');
+var lazyloading = require('utils/lazy_loading');
 
 /**
  * By Step 6, we have the following information stored in App.db and set on this
@@ -42,6 +43,7 @@ App.WizardStep6Controller = Em.Controller.extend({
    * false - slaves and clients
    */
   isMasters: false,
+  isLoaded: false,
 
   components: require('data/service_components'),
 
@@ -111,6 +113,7 @@ App.WizardStep6Controller = Em.Controller.extend({
     this.set('hosts', []);
     this.set('headers', []);
     this.clearError();
+    this.set('isLoaded', false);
   },
 
   /**
@@ -273,7 +276,7 @@ App.WizardStep6Controller = Em.Controller.extend({
    * Load all data needed for this module. Then it automatically renders in template
    */
   render: function () {
-    var hostsObj = Em.Set.create();
+    var hostsObj = [];
     var allHosts = this.getHostNames();
 
     var self = this;
@@ -303,9 +306,20 @@ App.WizardStep6Controller = Em.Controller.extend({
       hostsObj = this.renderSlaves(hostsObj);
     }
 
-    hostsObj.forEach(function (host) {
-      this.get('hosts').pushObject(host);
-    }, this);
+    if(hostsObj.length > 100) {
+      lazyloading.run({
+        destination: this.get('hosts'),
+        source: hostsObj,
+        context: this,
+        initSize: 20,
+        chunkSize: 50,
+        delay: 300
+      });
+    } else {
+      hostsObj.forEach(function (host) {
+        this.get('hosts').pushObject(host);
+      }, this);
+    }
     this.get('headers').forEach(function (header) {
       self.checkCallback(header.get('label'));
     });
@@ -474,4 +488,4 @@ App.WizardStep6Controller = Em.Controller.extend({
     return !isError;
   }
 
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/routes/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/installer.js b/ambari-web/app/routes/installer.js
index c90c82e..3cab49c 100644
--- a/ambari-web/app/routes/installer.js
+++ b/ambari-web/app/routes/installer.js
@@ -136,8 +136,13 @@ module.exports = Em.Route.extend({
     next: function (router) {
       var wizardStep1Controller = router.get('wizardStep1Controller');
       var installerController = router.get('installerController');
-      installerController.checkRepoURL(wizardStep1Controller);
-     // make sure got all validations feedback and no invalid url, then proceed
+      if (App.testMode) {
+        installerController.set('validationCnt', 0);
+        installerController.set('invalidCnt', 0);
+      } else {
+        installerController.checkRepoURL(wizardStep1Controller);
+      }
+      // make sure got all validations feedback and no invalid url, then proceed
       var myVar = setInterval(
         function(){
           var cnt = installerController.get('validationCnt');
@@ -240,7 +245,7 @@ module.exports = Em.Route.extend({
       router.setNavigationFlow('step5');
 
       var controller = router.get('installerController');
-      var wizardStep5Controller = router.get('wizardStep5Controller');
+      router.get('wizardStep5Controller').set('servicesMasters', []);
       controller.setCurrentStep('5');
       controller.loadAllPriorSteps();
       controller.connectOutlet('wizardStep5', controller.get('content'));
@@ -261,6 +266,7 @@ module.exports = Em.Route.extend({
       router.setNavigationFlow('step6');
 
       var controller = router.get('installerController');
+      router.get('wizardStep6Controller').set('hosts', []);
       controller.setCurrentStep('6');
       controller.loadAllPriorSteps();
       controller.connectOutlet('wizardStep6', controller.get('content'));

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/utils/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax.js b/ambari-web/app/utils/ajax.js
index 99c425a..a0cad02 100644
--- a/ambari-web/app/utils/ajax.js
+++ b/ambari-web/app/utils/ajax.js
@@ -840,7 +840,6 @@ var urls = {
     'type': 'PUT',
     'format': function (data) {
       return {
-        type: 'PUT',
         async: true,
         data: JSON.stringify(data.data)
       }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/utils/lazy_loading.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/lazy_loading.js b/ambari-web/app/utils/lazy_loading.js
new file mode 100644
index 0000000..3a439ba
--- /dev/null
+++ b/ambari-web/app/utils/lazy_loading.js
@@ -0,0 +1,98 @@
+/**
+ * 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.
+ */
+
+module.exports = {
+  /**
+   * Divide source array in chunks, then push each chunk into destination array
+   * with delay time one by one. So then destination array gets more and more items
+   * till all items are loaded.
+   * @param options
+   * options.initSize - number of items which will be pushed in array immediately
+   * options.chunkSize - number of items which will be pushed after defined delay
+   * options.delay - interval between each chunk push
+   * options.destination - array where items will be pushed
+   * options.source - source of items
+   * options.context - the object that should know when data is completely loaded,
+   * lazy loading will define "isLoaded" property for context object and update it
+   */
+  run: function (options) {
+    var initSize = options.initSize || 25,
+      chunkSize = options.chunkSize || 50,
+      delay = options.delay || 300,
+      destination = options.destination,
+      source = options.source,
+      context = options.context,
+      chunks;
+    if (Array.isArray(destination) && Array.isArray(source)) {
+      destination.pushObjects(source.slice(0, initSize));
+      if(source.length > initSize) {
+        chunks = this.divideIntoChunks(source.slice(initSize, source.length), chunkSize);
+        this.pushChunk(chunks, 0, delay, destination, context);
+      } else {
+        context.set('isLoaded', true);
+      }
+    } else {
+      console.error('Lazy loading: source or destination has incorrect value');
+    }
+  },
+
+  /**
+   * push chunks into destination array in delay time
+   * @param chunks
+   * @param index
+   * @param delay
+   * @param destination
+   * @param context
+   */
+  pushChunk: function (chunks, index, delay, destination, context) {
+    var self = this;
+    setTimeout(function () {
+      destination.pushObjects(chunks[index]);
+      if (chunks.length === (index + 1)) {
+        context.set('isLoaded', true);
+      }
+      index++;
+      self.pushChunk(chunks, index, delay, destination, context);
+    }, delay);
+  },
+
+  /**
+   * divide source array into chunks
+   * @param source
+   * @param chunkSize
+   * @return {Array}
+   */
+  divideIntoChunks: function (source, chunkSize) {
+    var chunk = [];
+    var chunks = [];
+    var counter = 0;
+    source.forEach(function (item) {
+      counter++;
+      chunk.push(item);
+      if (counter === chunkSize) {
+        chunks.push(chunk);
+        chunk = [];
+        counter = 0;
+      }
+    });
+    if (chunk.length > 0) {
+      chunks.push(chunk);
+    }
+    return chunks;
+  }
+};

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/views/wizard/step3_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step3_view.js b/ambari-web/app/views/wizard/step3_view.js
index cfa6749..f603059 100644
--- a/ambari-web/app/views/wizard/step3_view.js
+++ b/ambari-web/app/views/wizard/step3_view.js
@@ -25,7 +25,7 @@ App.WizardStep3View = Em.View.extend({
   category: '',
 
   didInsertElement: function () {
-    this.get('controller').navigateStep();
+    this.get('controller').loadStep();
   },
 
   message:'',

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/app/views/wizard/step5_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step5_view.js b/ambari-web/app/views/wizard/step5_view.js
index a9419b6..d735b8a 100644
--- a/ambari-web/app/views/wizard/step5_view.js
+++ b/ambari-web/app/views/wizard/step5_view.js
@@ -18,6 +18,7 @@
 
 
 var App = require('app');
+var lazyloading = require('utils/lazy_loading');
 
 App.WizardStep5View = Em.View.extend({
 
@@ -35,10 +36,30 @@ App.SelectHostView = Em.Select.extend({
   selectedHost:null,
   componentName:null,
   attributeBindings:['disabled'],
+  isLoaded: false,
 
   change:function () {
     this.get('controller').assignHostToMaster(this.get("componentName"), this.get("value"), this.get("zId"));
   },
+  click: function () {
+    var source = [];
+    var selectedHost = this.get('selectedHost');
+    var content = this.get('content');
+    if (!this.get('isLoaded') && this.get('controller.isLazyLoading')) {
+      //filter out hosts, which already pushed in select
+      source = this.get('controller.hosts').filter(function(_host){
+        return !content.someProperty('host_name', _host.host_name);
+      }, this);
+      lazyloading.run({
+        destination: this.get('content'),
+        source: source,
+        context: this,
+        initSize: 30,
+        chunkSize: 50,
+        delay: 200
+      });
+    }
+  },
 
   didInsertElement:function () {
     this.set("value", this.get("selectedHost"));

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/5c767210/ambari-web/test/utils/lazy_loading_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/lazy_loading_test.js b/ambari-web/test/utils/lazy_loading_test.js
new file mode 100644
index 0000000..f5120ee
--- /dev/null
+++ b/ambari-web/test/utils/lazy_loading_test.js
@@ -0,0 +1,110 @@
+/**
+ * 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 lazyLoading = require('utils/lazy_loading');
+
+describe('lazy_loading', function () {
+
+  describe('#run', function () {
+    var context = Em.Object.create({isLoaded: false});
+    var options = {
+      destination: [],
+      source: [{'test':'test'}],
+      context: context
+    };
+    it('load one item', function () {
+      lazyLoading.run(options);
+      expect(options.destination[0]).to.eql(options.source[0]);
+      expect(context.get('isLoaded')).to.equal(true);
+    });
+
+    var testsInfo = [
+      {
+        title: 'load 11 item with initSize - 11',
+        result: true,
+        initSize: 11,
+        destinationLength: 11,
+        destination: [],
+        source: [{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}],
+        context: Em.Object.create()
+      },
+      {
+        title: 'load 11 item with initSize - 12',
+        result: true,
+        initSize: 12,
+        destinationLength: 11,
+        destination: [],
+        source: [{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}],
+        context: Em.Object.create()
+      },
+      {//items will be completely loaded on next iteration of pushing chunk
+        title: 'load 11 item with initSize - 10',
+        result: false,
+        initSize: 10,
+        destinationLength: 10,
+        destination: [],
+        source: [{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}],
+        context: Em.Object.create({isLoaded: false})
+      }
+    ];
+    testsInfo.forEach(function(test){
+      it(test.title, function () {
+        lazyLoading.run(test);
+        expect(test.destinationLength).to.equal(test.destination.length);
+        expect(test.context.get('isLoaded')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#divideIntoChunks', function () {
+    var testsInfo = [
+      {
+        title: 'load 11 item with chunkSize - 3',
+        chunkSize: 3,
+        source: [{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}],
+        chunks: [[{i:1}, {i:2}, {i:3}], [{i:4}, {i:5}, {i:6}], [{i:7}, {i:8}, {i:9}], [{i:10},{i:11}]]
+      },
+      {
+        title: 'load 11 item with chunkSize - 0',
+        chunkSize: 0,
+        source: [{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}],
+        chunks: [[{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}]]
+      },
+      {
+        title: 'load 11 item with chunkSize - 1',
+        chunkSize: 1,
+        source: [{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}],
+        chunks: [[{i:1}], [{i:2}], [{i:3}], [{i:4}], [{i:5}], [{i:6}], [{i:7}], [{i:8}], [{i:9}], [{i:10}], [{i:11}]]
+      },
+      {
+        title: 'load 11 item with chunkSize - 11',
+        chunkSize: 0,
+        source: [{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}],
+        chunks: [[{i:1}, {i:2}, {i:3}, {i:4}, {i:5}, {i:6}, {i:7}, {i:8}, {i:9}, {i:10},{i:11}]]
+      }
+    ];
+    testsInfo.forEach(function(test){
+      it(test.title, function () {
+        var chunks = lazyLoading.divideIntoChunks(test.source, test.chunkSize);
+        expect(chunks).to.eql(test.chunks);
+      });
+    });
+  });
+
+
+});