text: "use strict";
  
  var $ = require("jquery"),
      config = require("core/config"),
      formatHelper = require("format_helper"),
      errors = require("ui/widget/ui.errors"),
      commonUtils = require("core/utils/common"),
      DataSource = require("data/data_source/data_source").DataSource,
      ArrayStore = require("data/array_store"),
      dataGridMocks = require("../../helpers/dataGridMocks.js"),
      setupDataGridModules = dataGridMocks.setupDataGridModules,
      MockGridDataSource = dataGridMocks.MockGridDataSource;
  
  require("ui/data_grid/ui.data_grid");
  
  var createDataSource = function(data, storeOptions, dataSourceOptions) {
      var arrayStore = new ArrayStore(storeOptions ? $.extend(true, { data: data }, storeOptions) : data);
      var dataSource = new DataSource($.extend(true, { store: arrayStore, requireTotalCount: true, _preferSync: true }, dataSourceOptions));
      return dataSource;
  };
  
  var setupModule = function() {
      setupDataGridModules(this, ['data', 'virtualScrolling', 'columns', 'filterRow', 'search', 'editing', 'grouping', 'headerFilter']);
  
      this.applyOptions = function(options) {
          $.extend(this.options, options);
          this.columnsController.init();
      };
      this.clock = sinon.useFakeTimers();
      this.editingController.getFirstEditableCellInRow = function() { return $([]); };
  };
  
  var teardownModule = function() {
      this.dispose();
      this.clock.restore();
  };
  
  QUnit.module("Initialization", { beforeEach: setupModule, afterEach: teardownModule });
  
  QUnit.test("No initialization", function(assert) {
      assert.deepEqual(this.dataController.items(), []);
      assert.ok(!this.dataController.isLoading());
      assert.ok(this.dataController.isLoaded());
  });
  
  QUnit.test("Initialize from dataSource with all visible columns", function(assert) {
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      assert.equal(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', '55-55-55']);
      assert.deepEqual(this.dataController.items()[1].values, ['Dan', '98-75-21']);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Alex', phone: '55-55-55' });
      assert.deepEqual(this.dataController.items()[1].data, { name: 'Dan', phone: '98-75-21' });
  });
  
  QUnit.test("Initialize from dataSource when dataSource has null item", function(assert) {
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' },
          null
      ];
  
      var dataSource = createDataSource(array);
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      assert.equal(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', '55-55-55']);
      assert.deepEqual(this.dataController.items()[1].values, ['Dan', '98-75-21']);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Alex', phone: '55-55-55' });
      assert.deepEqual(this.dataController.items()[1].data, { name: 'Dan', phone: '98-75-21' });
  });
  
  QUnit.test("Initialize from options with invisible columns", function(assert) {
      var array = [
          { name: 'Alex', age: 20, phone: '55-55-55' },
          { name: 'Dan', age: 30, phone: '98-75-21' }
      ];
      var dataSource = createDataSource(array);
  
      this.applyOptions({ columns: ["name", { dataField: 'age', visible: false }, 'phone'] });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      assert.equal(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', '55-55-55']);
      assert.deepEqual(this.dataController.items()[1].values, ['Dan', '98-75-21']);
  });
  
  QUnit.test("changed on initialize", function(assert) {
      var changedCount = 0;
      var lastArgs;
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' }
      ];
      var dataSource = createDataSource(array);
  
  
      this.dataController.changed.add(function(args) {
          changedCount++;
          lastArgs = args;
      });
      assert.strictEqual(changedCount, 0);
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //assert
      assert.strictEqual(changedCount, 1);
      assert.ok(lastArgs);
      assert.strictEqual(lastArgs.changeType, 'refresh');
      assert.equal(lastArgs.items.length, 2);
      assert.ok(lastArgs.items[0].data);
      assert.ok(lastArgs.items[0].values);
  });
  
  //B255430
  QUnit.test("Call changed after all columnsChanged", function(assert) {
      //arrange
      var that = this,
          changedCount = 0,
          columnsChangedCount = 0,
          array = [
              { name: 'Alex', phone: '55-55-55' },
              { name: 'Dan', phone: '98-75-21' }
          ],
          dataSource = createDataSource(array);
  
      that.dataController.setDataSource(dataSource);
      dataSource.load();
  
      that.dataController.changed.add(function() {
          changedCount++;
  
          //assert
          assert.equal(columnsChangedCount, 1, "columnChangedCount");
      });
  
      that.columnsController.columnsChanged.add(function() {
          columnsChangedCount++;
  
          //assert
          assert.equal(changedCount, 0, "changedCount");
      });
  
      //act
      that.columnsController.columnOption(0, 'visible', false);
  
      //assert
      assert.equal(changedCount, 1, "changedCount");
      assert.equal(columnsChangedCount, 1, "columnChangedCount");
  });
  
  QUnit.test("events rising on second initialize shared dataSource", function(assert) {
      var changedCount = 0;
      var dataSource = createDataSource([]);
  
      this.applyOptions({
          dataSource: dataSource
      });
  
      this.dataController.optionChanged({ name: "dataSource" });
  
      this.dataController.changed.add(function(args) {
          changedCount++;
      });
  
      //act
      this.dataController.optionChanged({ name: "dataSource" });
  
      //assert
      assert.strictEqual(changedCount, 1, 'changed called');
      assert.ok(!dataSource._disposed, 'dataSource is not disposed');
  });
  
  QUnit.test("events rising on second initialize not shared dataSource", function(assert) {
      var changedCount = 0;
  
      this.applyOptions({
          dataSource: []
      });
  
      this.dataController.optionChanged({ name: "dataSource" });
  
      var dataSource = this.dataController.dataSource()._dataSource;
  
      this.dataController.changed.add(function(args) {
          changedCount++;
      });
  
      //act
      this.dataController.optionChanged({ name: "dataSource" });
  
      //assert
      assert.ok(dataSource, 'dataSource created');
      assert.ok(dataSource._disposed, 'dataSource is disposed');
      assert.strictEqual(changedCount, 1, 'changed called');
  });
  
  QUnit.test("update rows on columnsChanged (changeType == 'columns')", function(assert) {
      var changedCount = 0;
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' }
      ];
      var dataSource = createDataSource(array);
  
      this.dataController.changed.add(function(args) {
          changedCount++;
      });
  
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      var rows = this.dataController.items();
      this.columnsController.columnOption(1, 'visible', false);
  
      assert.notStrictEqual(this.dataController.items(), rows);
      assert.strictEqual(changedCount, 2);
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex']);
      assert.deepEqual(this.dataController.items()[1].values, ['Dan']);
  });
  
  //T134124
  QUnit.test("update rows on columnsChanged when asyncLoadEnabled and column that need converting exists (changeType == 'columns')", function(assert) {
      var changedCount = 0;
      var array = [
          { name: 'Alex', phone: '55-55-55', age: "19" },
          { name: 'Dan', phone: '98-75-21', age: "23" }
      ];
  
      this.options.loadingTimeout = 0;
      this.options.customizeColumns = function(columns) {
          columns[2].dataType = 'number';
      };
      var dataSource = createDataSource(array);
  
      this.dataController.changed.add(function(args) {
          changedCount++;
      });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.clock.tick();
  
      var items = this.dataController.items();
      this.columnsController.columnOption(1, 'visible', false);
  
      this.clock.tick();
  
      assert.notStrictEqual(this.dataController.items(), items);
      assert.strictEqual(changedCount, 2);
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 19]);
      assert.deepEqual(this.dataController.items()[1].values, ['Dan', 23]);
  });
  
  //B253046
  QUnit.test("Load sorting from state when columns generated by dataSource", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
      var dataSource = createDataSource(array);
  
      this.applyOptions({
          commonColumnSettings: { allowSorting: true }
      });
  
      this.columnsController.setUserState([{ dataField: 'name', visible: true, sortOrder: 'desc', sortIndex: 0, index: 0 }, { dataField: 'age', visible: true, sortOrder: 'asc', sortIndex: 1, index: 1 }]);
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //assert
      assert.deepEqual(this.dataController.items()[0].values, ['Dan', 25]);
      assert.deepEqual(this.dataController.items()[1].values, ['Bob', 20]);
      assert.deepEqual(this.dataController.items()[2].values, ['Alex', 30]);
  });
  
  //B254110
  QUnit.test("set isExpanded group parameters to dataSource on initialization", function(assert) {
      //arrange
      var changedCallCount = 0;
      this.applyOptions({
          commonColumnSettings: { autoExpandGroup: true },
          dataSource: {
              store: [
                  { name: 'Alex', age: 30 },
                  { name: 'Dan', age: 25 },
                  { name: 'Bob', age: 20 }
              ],
              group: ['name', 'age'],
              asyncLoadEnabled: false
          },
          columns: ['name', 'age']
      });
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
  
      //act
      this.dataController._refreshDataSource();
  
  
      //assert
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: 'name', isExpanded: true, desc: false }, { selector: 'age', isExpanded: true, desc: false }]);
      assert.equal(this.dataController.items()[0].data.key, 'Alex');
      assert.equal(this.dataController.items()[1].data.key, 30);
      assert.equal(changedCallCount, 1, "changed called one time"); //T122785
  });
  
  //T221453
  QUnit.test("grouping with autoExpandAll is false", function(assert) {
      //arrange
      var changedCallCount = 0;
      this.applyOptions({
          dataSource: {
              store: [
                  { name: 'Alex', age: 30 },
                  { name: 'Dan', age: 25 },
                  { name: 'Bob', age: 20 }
              ],
              group: ['name'],
              asyncLoadEnabled: false
          },
          grouping: { autoExpandAll: false },
          columns: ['name', 'age']
      });
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController._refreshDataSource();
  
      //assert
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: 'name', isExpanded: false, desc: false }]);
      assert.equal(this.dataController.items()[0].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[0].data.collapsedItems, [{ name: 'Alex', age: 30 }]);
      assert.equal(this.dataController.items()[0].data.items, null);
      assert.equal(changedCallCount, 1, "changed called one time");
  });
  
  //B254110
  QUnit.test("collapseAll when grouped column with dataType", function(assert) {
      //arrange
      var changedCallCount = 0,
          loadingCount = 0;
  
      this.applyOptions({
          commonColumnSettings: { autoExpandGroup: true },
          dataSource: {
              store: {
                  type: "array",
                  onLoading: function() {
                      loadingCount++;
                  },
                  data: [
                      { name: 'Alex', age: 30 },
                      { name: 'Dan', age: 25 },
                      { name: 'Bob', age: 20 }
                  ]
              },
              group: ['name'],
              asyncLoadEnabled: false
          },
          columns: [{ dataField: 'name', dataType: 'string' }, 'age']
      });
  
      //act
      this.dataController._refreshDataSource();
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
      assert.equal(loadingCount, 1, "loading called one time");
  
      //assert
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: "name", isExpanded: true, desc: false }]);
      assert.equal(this.dataController.items()[0].data.key, 'Alex');
      assert.equal(this.dataController.items()[0].data.items.length, 1);
  
      //act
      this.dataController.collapseAll();
  
      this.clock.tick();
  
      //assert
      assert.equal(changedCallCount, 1, "changed called one time");
      //T372049
      assert.equal(loadingCount, 1, "loading is not called on collapseAll");
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: "name", isExpanded: false, desc: false }]);
      assert.equal(this.dataController.items()[0].data.key, 'Alex');
      assert.strictEqual(this.dataController.items()[0].data.items, null);
  });
  
  //T372049
  QUnit.test("collapseAll when remoteOperations with grouping and without paging", function(assert) {
      //arrange
      var changedCallCount = 0,
          loadingArgs = [];
  
      this.applyOptions({
          commonColumnSettings: { autoExpandGroup: true },
          remoteOperations: { grouping: true, sorting: true },
          dataSource: {
              store: {
                  type: "array",
                  onLoading: function(e) {
                      loadingArgs.push(e);
                  },
                  data: [
                      { name: 'Alex', age: 30 },
                      { name: 'Dan', age: 25 },
                      { name: 'Bob', age: 20 }
                  ]
              },
              group: ['name'],
              asyncLoadEnabled: false
          },
          columns: ['name', 'age']
      });
  
      //act
      this.dataController._refreshDataSource();
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
      assert.equal(loadingArgs.length, 1, "loading called one time");
      assert.deepEqual(loadingArgs[0].group, [{ selector: "name", isExpanded: true, desc: false }], "loading group arg");
  
      //assert
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: "name", isExpanded: true, desc: false }]);
      assert.equal(this.dataController.items()[0].data.key, 'Alex');
      assert.equal(this.dataController.items()[0].data.items.length, 1);
  
      //act
      this.dataController.collapseAll();
  
      this.clock.tick();
  
      //assert
      assert.equal(changedCallCount, 1, "changed called one time after collapseAll");
      //T372049
      assert.equal(loadingArgs.length, 1, "loading is not called after collapseAll");
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: "name", isExpanded: false, desc: false }]);
      assert.equal(this.dataController.items()[0].data.key, 'Alex');
      assert.strictEqual(this.dataController.items()[0].data.items, null);
  });
  
  QUnit.test("collapseAll when several columns grouped", function(assert) {
      //arrange
      var changedCallCount = 0;
      this.applyOptions({
          commonColumnSettings: { autoExpandGroup: true },
          dataSource: {
              store: [
                  { name: 'Alex', age: 30 },
                  { name: 'Dan', age: 25 },
                  { name: 'Bob', age: 20 }
              ],
              group: ['name', 'age'],
              asyncLoadEnabled: false
          },
          columns: ['name', 'age']
      });
  
      this.dataController._refreshDataSource();
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.collapseAll();
      this.clock.tick();
  
      this.dataController.expandRow(['Alex']);
  
      //assert
      assert.equal(changedCallCount, 2, "changed called two times");
  
      var items = this.dataController.items();
      assert.deepEqual(items[0].key, ['Alex'], 'row 0 key');
      assert.ok(items[0].isExpanded, 'row 0 isExpanded');
      assert.deepEqual(items[1].key, ['Alex', 30], 'row 1 key');
      assert.ok(!items[1].isExpanded, 'row 1 isExpanded');
      assert.deepEqual(items[2].key, ['Bob'], 'row 2 key');
      assert.ok(!items[2].isExpanded, 'row 2 isExpanded');
  });
  
  QUnit.test("the number of visible items should be identical after expandAll/collapseAll", function(assert) {
      var dataItems = [];
  
      $.each(TEN_NUMBERS, function(value) {
          dataItems.push({
              "field1": "group" + value,
              "field2": "firstItem"
          });
  
          dataItems.push({
              "field1": "group" + value,
              "field2": "secondItem"
          });
      });
  
      this.applyOptions({
          scrolling: { mode: 'virtual' },
          dataSource: dataItems,
          columns: [
              {
                  "name": "field1",
                  "dataField": "field1",
                  groupIndex: 0
              },
              {
                  "name": "field2",
                  "dataField": "field2"
              }
          ]
      });
  
      this.dataController._refreshDataSource();
  
      var itemsCount = this.dataController.items().length;
  
      //act
      this.dataController.expandAll();
      this.dataController.collapseAll();
  
      //assert
      assert.equal(this.dataController.items().length, itemsCount, "There are no excess items");
  });
  
  //B254274
  QUnit.test("sortOrder in column options and group parameters in dataSource", function(assert) {
      //arrange
      this.applyOptions({
          commonColumnSettings: { autoExpandGroup: true },
          dataSource: {
              store: [
                  { team: 'internal', name: 'Alex', age: 30 },
                  { team: 'internal', name: 'Dan', age: 25 },
                  { team: 'internal', name: 'Bob', age: 20 },
                  { team: 'public', name: 'Alice', age: 19 }
              ],
              group: ['team'],
              asyncLoadEnabled: false
          },
          columns: ['team', 'name', { dataField: 'age', sortOrder: 'asc' }]
      });
  
      //act
      this.dataController._refreshDataSource();
  
      //assert
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: 'team', isExpanded: true, desc: false }]);
      assert.equal(this.dataController.items()[0].data.key, 'internal');
      assert.equal(this.dataController.items()[1].data.name, 'Bob');
  });
  
  QUnit.test("update sorting on columnsChanged (changeType == 'sorting')", function(assert) {
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.applyOptions({
          commonColumnSettings: { allowSorting: true },
          sorting: { mode: 'single' }
      });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.columnsController.changeSortOrder(0, 'asc');
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 30]);
      assert.deepEqual(this.dataController.items()[1].values, ['Bob', 20]);
      assert.deepEqual(this.dataController.items()[2].values, ['Dan', 25]);
  });
  
  QUnit.test("reset sorting on columnsChanged (changeType == 'sorting')", function(assert) {
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
      this.applyOptions({
          commonColumnSettings: { allowSorting: true },
          sorting: { mode: 'single' }
      });
  
      var dataSource = createDataSource(array, { key: 'name' }, { sort: 'name' });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      assert.ok(this.dataController.dataSource().sort(), 'sort parameters');
  
      //act
      this.columnsController.changeSortOrder(0, 'none');
  
      //assert
      assert.ok(!this.dataController.dataSource().sort(), 'no sort parameters');
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 30]);
      assert.deepEqual(this.dataController.items()[1].values, ['Dan', 25]);
      assert.deepEqual(this.dataController.items()[2].values, ['Bob', 20]);
  });
  
  QUnit.test("update grouping on columnsChanged (changeType == 'grouping')", function(assert) {
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.applyOptions({
          commonColumnSettings: { allowSorting: true },
          customizeColumns: function(columns) {
              columns[1].groupIndex = 0;
          }
      });
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      assert.ok(this.dataController._dataSource.group());
      assert.equal(this.dataController.items()[0].rowType, 'group');
  });
  
  QUnit.test("Load group from state when columns generated by dataSource", function(assert) {
      //arrange
      this.options.remoteOperations = { filtering: true, sorting: true, paging: true };
      this.options.groupPanel = { visible: true, allowColumnDragging: true };
      this.options.grouping = { autoExpandAll: true };
  
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
      var dataSource = createDataSource(array);
      this.columnsController.setUserState([{ dataField: 'name', visible: true, groupIndex: 0, index: 0 }, { dataField: 'age', visible: true, index: 1 }]);
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //assert
      assert.equal(this.dataController.items().length, 6);
      assert.deepEqual(this.dataController.items()[0].rowType, 'group');
      assert.deepEqual(this.dataController.items()[1].rowType, 'data');
      assert.deepEqual(this.dataController.items()[2].rowType, 'group');
      assert.deepEqual(this.dataController.items()[3].rowType, 'data');
      assert.deepEqual(this.dataController.items()[4].rowType, 'group');
      assert.deepEqual(this.dataController.items()[5].rowType, 'data');
  });
  
  QUnit.test("Reset sort/group parameters from user state for removed columns", function(assert) {
      //arrange
      var dataSource = [
              { age: 30 },
              { age: 20 },
              { age: 25 }
      ];
  
      //user state has options for removed column with dataField 'name'
      this.columnsController.setUserState([{ dataField: 'name', visible: true, groupIndex: 0, index: 0 }, { dataField: 'age', visible: true, sortIndex: 0, sortOrder: 'asc', index: 1 }]);
  
      this.applyOptions({ dataSource: dataSource });
  
      //act
      this.dataController.optionChanged({
          name: "dataSource",
          value: dataSource
      });
  
      //assert
      assert.deepEqual(this.dataController._dataSource.sort(), [{ selector: "age", desc: false }], 'sort from user state is applied by dataField');
      assert.deepEqual(this.dataController._dataSource.group(), null, 'group from user state not applied because dataSource not has name column');
      assert.deepEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].values, [20]);
      assert.deepEqual(this.dataController.items()[1].values, [25]);
      assert.deepEqual(this.dataController.items()[2].values, [30]);
  });
  
  //B253402
  QUnit.test("reset grouping on columnsChanged (changeType == 'grouping')", function(assert) {
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
      var dataSource = createDataSource(array, { key: 'name' }, { group: 'name' });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      assert.ok(this.dataController.dataSource().group());
      //act
      this.columnsController.moveColumn(0, 0, 'group', 'header');
  
      //assert
      assert.ok(!this.dataController.dataSource().group());
      assert.equal(this.dataController.items()[0].rowType, 'data');
  });
  
  QUnit.test("update sorting on columnsChanged (changeType == 'sorting'). Refresh current page", function(assert) {
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.applyOptions({
          commonColumnSettings: { allowSorting: true },
          sorting: { mode: 'single' }
      });
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      this.dataController.pageIndex(1);
  
      //act
      this.columnsController.changeSortOrder(0, 'asc');
  
      assert.equal(this.dataController.pageIndex(), 1);
      assert.equal(this.dataController.items().length, 1);
      assert.deepEqual(this.dataController.items()[0].values, ['Dan', 25]);
  });
  
  QUnit.test("update sorting on columnsChanged when sortingMethod defined(changeType == 'sorting'). Refresh current page", function(assert) {
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.applyOptions({
          commonColumnSettings: { allowSorting: true },
          columns: [{
              dataField: "name", calculateSortValue: function(data) {
                  return $.inArray(data.name, ['Dan', 'Bob', 'Alex']);
              }
          }, "age"],
          sorting: { mode: 'single' }
      });
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
  
      //act
      this.columnsController.changeSortOrder(0, 'asc');
  
      assert.equal(this.dataController.pageIndex(), 0);
      assert.equal(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].values, ['Dan', 25]);
  });
  
  QUnit.test("update sorting on columnsChanged (changeType == 'sorting'). Several updates", function(assert) {
      var array = [
          { name: 'Alex', age: 30 },
          { name: 'Dan', age: 25 },
          { name: 'Bob', age: 20 }
      ];
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.applyOptions({
          commonColumnSettings: { allowSorting: true },
          sorting: { mode: 'single' }
      });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.columnsController.changeSortOrder(0, 'asc');
      this.columnsController.changeSortOrder(1, 'asc');
  
      assert.deepEqual(this.dataController.items()[0].values, ['Bob', 20]);
      assert.deepEqual(this.dataController.items()[1].values, ['Dan', 25]);
      assert.deepEqual(this.dataController.items()[2].values, ['Alex', 30]);
  });
  
  //B233497
  QUnit.test("changed when ArrayStore with datetime fields in string format", function(assert) {
      var columnsChangedArgs = [];
      var changedCount = 0;
      var dataController = this.dataController;
  
      var dataSource = createDataSource([
          { name: 'Alex', birthDate: '1987/5/5' },
          { name: 'Dan', birthDate: '1985/3/21' }
      ], {}, { asyncLoadEnabled: true });
  
      this.applyOptions({
          columns: ['Alex', { dataField: 'birthDate', dataType: 'date' }]
      });
      dataController.changed.add(function() {
          changedCount++;
      });
      this.columnsController.columnsChanged.add(function(e) {
          columnsChangedArgs.push(e);
      });
      dataController.setDataSource(dataSource);
      dataSource.load();
      this.clock.tick();
  
      //assert
      assert.deepEqual(dataController.items()[0].values[1], new Date(1987, 4, 5));
      assert.equal(changedCount, 1);
      assert.equal(columnsChangedArgs.length, 1);
      assert.equal(columnsChangedArgs[0].changeTypes.length, 1);
      assert.ok(columnsChangedArgs[0].changeTypes.columns);
  });
  
  QUnit.test("changed when ArrayStore with datetime fields in string format and sorting defined", function(assert) {
      var columnsChangedArgs = [];
      var changedCount = 0;
      var dataController = this.dataController;
  
      var dataSource = createDataSource([
          { name: 'Alex', birthDate: '1987/5/5' },
          { name: 'Dan', birthDate: '1985/3/21' }
      ], {}, { asyncLoadEnabled: true });
  
      this.applyOptions({
          columns: ['Alex', { dataField: 'birthDate', dataType: 'date', sortOrder: 'asc' }]
      });
      dataController.changed.add(function() {
          changedCount++;
      });
      this.columnsController.columnsChanged.add(function(e) {
          columnsChangedArgs.push(e);
      });
      dataController.setDataSource(dataSource);
      dataSource.load();
      this.clock.tick();
  
      //assert
      assert.deepEqual(dataController.items()[0].values[1], new Date(1985, 2, 21));
      //T122785
      assert.equal(changedCount, 1, 'one load');
      assert.equal(columnsChangedArgs.length, 1);
      //T174640
      assert.equal(columnsChangedArgs[0].changeTypes.length, 1);
      assert.ok(columnsChangedArgs[0].changeTypes.columns);
  });
  
  QUnit.test("changed when ArrayStore with datetime fields in string format and sorting defined and serializationFormat defined", function(assert) {
      var columnsChangedArgs = [];
      var changedCount = 0;
      var dataController = this.dataController;
  
      var dataSource = createDataSource([
          { name: 'Alex', birthDate: '1987-05-05T00:00:00Z' },
          { name: 'Dan', birthDate: '1985-03-21T00:00:00Z' }
      ], {}, { asyncLoadEnabled: true });
  
      this.applyOptions({
          columns: ['Alex', { dataField: 'birthDate', dataType: 'date', serializationFormat: "yyyy-MM-ddTHH:mm:ss'Z'", sortOrder: 'asc' }]
      });
      dataController.changed.add(function() {
          changedCount++;
      });
      this.columnsController.columnsChanged.add(function(e) {
          columnsChangedArgs.push(e);
      });
      dataController.setDataSource(dataSource);
      dataSource.load();
      this.clock.tick();
  
      //assert
      assert.deepEqual(dataController.items()[0].values[1], new Date(Date.UTC(1985, 2, 21)));
      assert.equal(changedCount, 1, 'one load');
      assert.equal(columnsChangedArgs.length, 1);
      assert.equal(columnsChangedArgs[0].changeTypes.length, 1);
      assert.ok(columnsChangedArgs[0].changeTypes.columns);
  });
  
  QUnit.test("update lookup items on refresh", function(assert) {
      var changedCount = 0;
      var dataController = this.dataController;
  
      var lookupLoadResult = [];
  
      this.options.loadingTimeout = 0;
  
      var dataSource = createDataSource([
          { name: 'Alex', birthDate: '5/5/1987' },
          { name: 'Dan', birthDate: '3/21/1985' }
      ]);
  
      this.applyOptions({
          columns: [{
              dataField: 'name', lookup: {
                  valueExpr: "test",
                  dataSource: {
                      load: function() {
                          return lookupLoadResult;
                      }
                  }
              }
          }]
      });
      dataController.setDataSource(dataSource);
      dataSource.load();
      this.clock.tick();
  
      lookupLoadResult = $.Deferred();
      //act
  
      dataController.changed.add(function(args) {
          changedCount++;
      });
  
      var refreshed = false;
  
      //act
      dataController.refresh().done(function() {
          refreshed = true;
      });
  
      this.clock.tick();
      //assert
      assert.equal(changedCount, 0, 'changed call count');
      assert.ok(!refreshed, 'not refreshed');
  
      //act
      lookupLoadResult.resolve([]);
  
      //assert
      assert.equal(changedCount, 0, 'changed call count');
      assert.ok(!refreshed, 'not refreshed');
      this.clock.tick();
      assert.equal(changedCount, 1, 'changed call count');
      assert.ok(refreshed, 'refreshed');
  });
  
  QUnit.test("Not Update lookup items when no valueExpr", function(assert) {
      var changedCount = 0,
          dataController = this.dataController,
          lookupLoadCount = 0;
  
      var dataSource = createDataSource([
          { name: 'Alex', birthDate: '5/5/1987' },
          { name: 'Dan', birthDate: '3/21/1985' }
      ], {}, { asyncLoadEnabled: true });
  
      this.applyOptions({
          columns: [{
              dataField: 'name', lookup: {
                  dataSource: {
                      load: function() {
                          lookupLoadCount++;
                          return [];
                      }
                  }
              }
          }]
      });
      dataController.setDataSource(dataSource);
  
      dataController.changed.add(function(args) {
          changedCount++;
      });
  
      //act
      dataSource.load();
      this.clock.tick();
  
      //assert
      assert.equal(changedCount, 1, 'changed call count');
      assert.equal(lookupLoadCount, 0, 'lookup load count');
  });
  
  QUnit.test("Not Update lookup items on change sorting", function(assert) {
      var changedCount = 0,
          dataController = this.dataController,
          lookupLoadCount = 0;
  
      var dataSource = createDataSource([
          { name: 'Alex', birthDate: '5/5/1987' },
          { name: 'Dan', birthDate: '3/21/1985' }
      ], {}, { asyncLoadEnabled: true });
  
      this.applyOptions({
          sorting: { mode: 'single' },
          columns: [{
              allowSorting: true,
              dataField: 'name', lookup: {
                  valueExpr: "test",
                  dataSource: {
                      load: function() {
                          lookupLoadCount++;
                          return [];
                      }
                  }
              }
          }]
      });
      dataController.setDataSource(dataSource);
      dataSource.load();
      this.clock.tick();
  
  
      dataController.changed.add(function(args) {
          changedCount++;
      });
  
      changedCount = 0;
      lookupLoadCount = 0;
  
  
      //act
      this.columnsController.changeSortOrder(0, 'desc');
      this.clock.tick();
  
      //assert
      assert.equal(changedCount, 1, 'changed call count');
      assert.equal(lookupLoadCount, 0, 'lookup load count');
  });
  
  QUnit.test("byKey from loaded items", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Sam', phone: '66-66-66' },
          { name: 'Dan', phone: '98-75-21' }
          ],
          data;
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act
      this.dataController.byKey("Alex").done(function(result) {
          data = result;
      });
  
      //assert
      assert.equal(this.dataController.items().length, 2, "count loaded items");
      assert.deepEqual(data, { name: "Alex", phone: "55-55-55" }, "data");
  });
  
  QUnit.test("byKey from data store", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Sam', phone: '66-66-66' },
          { name: 'Dan', phone: '98-75-21' }
          ],
          data;
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act
      this.dataController.byKey("Dan").done(function(result) {
          data = result;
      });
  
      //assert
      assert.equal(this.dataController.items().length, 2, "count loaded items");
      assert.deepEqual(data, { name: "Dan", phone: "98-75-21" }, "data");
  });
  
  QUnit.test("byKey when not has key", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Sam', phone: '66-66-66' },
          { name: 'Dan', phone: '98-75-21' }
          ],
          data;
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act
      this.dataController.byKey("Test").done(function(result) {
          data = result;
      });
  
      //assert
      assert.equal(this.dataController.items().length, 2, "count loaded items");
      assert.strictEqual(data, undefined, "not data");
  });
  
  QUnit.test("getDataByKeys from loaded items and data store", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Sam', phone: '66-66-66' },
          { name: 'Dan', phone: '98-75-21' }
          ],
          data;
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act
      this.dataController.getDataByKeys(["Alex", "Dan"]).done(function(result) {
          data = result;
      });
  
      //assert
      assert.equal(this.dataController.items().length, 2, "count loaded items");
      assert.equal(data.length, 2, "count data");
      assert.deepEqual(data[0], { name: "Alex", phone: "55-55-55" }, "data 1");
      assert.deepEqual(data[1], { name: "Dan", phone: "98-75-21" }, "data 2");
  });
  
  QUnit.test("getDataByKeys not has keys", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Sam', phone: '66-66-66' },
          { name: 'Dan', phone: '98-75-21' }
          ],
          data;
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act
      this.dataController.getDataByKeys(["Sam", "Test2"]).done(function(result) {
          data = result;
      });
  
      //assert
      assert.equal(this.dataController.items().length, 2, "count loaded items");
      assert.equal(data.length, 1, "count data");
      assert.deepEqual(data[0], { name: 'Sam', phone: '66-66-66' }, "data 1");
      assert.strictEqual(data[1], undefined, "not data 2");
  });
  
  QUnit.test("getKeyByRowIndex", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Sam', phone: '66-66-66' },
          { name: 'Dan', phone: '98-75-21' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act, assert
      assert.strictEqual(this.dataController.getKeyByRowIndex(0), "Alex");
      assert.strictEqual(this.dataController.getKeyByRowIndex(1), "Sam");
      assert.strictEqual(this.dataController.getKeyByRowIndex(5), undefined);
  });
  
  QUnit.test("getRowIndexByKey", function(assert) {
      //arrange
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Sam', phone: '66-66-66' },
          { name: 'Dan', phone: '98-75-21' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act, assert
      assert.strictEqual(this.dataController.getRowIndexByKey("Alex"), 0);
      assert.strictEqual(this.dataController.getRowIndexByKey("Sam"), 1);
      assert.strictEqual(this.dataController.getRowIndexByKey("Unknown"), -1);
  });
  
  //T174450
  QUnit.test("Assign loaded dataSource instance", function(assert) {
      //arrange
      var dataSource = new DataSource({
          store: [
              { age: 30 },
              { age: 20 },
              { age: 25 }
          ],
          group: 'age',
          _preferSync: true
      });
  
      dataSource.load();
  
      this.applyOptions({ dataSource: dataSource });
  
      //act
      this.dataController.optionChanged({
          name: "dataSource",
          value: dataSource
      });
  
      //assert
      assert.equal(this.dataController.items().length, 3);
      assert.equal(this.dataController.items()[0].rowType, 'group');
      assert.equal(this.dataController.items()[1].rowType, 'group');
      assert.equal(this.dataController.items()[2].rowType, 'group');
  });
  
  //T174450
  QUnit.test("Assign dataSource instance in loading", function(assert) {
      //arrange
      var dataSource = new DataSource({
          store: [
              { age: 30 },
              { age: 20 },
              { age: 25 }
          ],
          group: 'age'
      });
  
      dataSource.load();
  
      this.applyOptions({ dataSource: dataSource });
  
      //act
      this.dataController.optionChanged({
          name: "dataSource",
          value: dataSource
      });
  
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.items().length, 3);
      assert.equal(this.dataController.items()[0].rowType, 'group');
      assert.equal(this.dataController.items()[1].rowType, 'group');
      assert.equal(this.dataController.items()[2].rowType, 'group');
  });
  
  QUnit.test("Remote operations with ArrayStore", function(assert) {
      //arrange
      this.applyOptions({
          remoteOperations: "auto",
          dataSource: [
              { name: 'Alex', age: 20, phone: '55-55-55' },
              { name: 'Dan', age: 30, phone: '98-75-21' }
          ]
      });
  
      //actt
      this.dataController._refreshDataSource();
  
      //assert
      assert.deepEqual(this.dataController.dataSource().remoteOperations(), {}, "remote operations set correct");
  });
  
  QUnit.test("Enable all remote operations if groupPaging is true", function(assert) {
      //arrange
      this.applyOptions({
          remoteOperations: {
              groupPaging: true
          },
          dataSource: [
              { name: 'Alex', age: 20 }
          ]
      });
  
      //actt
      this.dataController._refreshDataSource();
  
      //assert
      assert.deepEqual(this.dataController.dataSource().remoteOperations(), {
          filtering: true,
          sorting: true,
          paging: true,
          grouping: true,
          summary: true,
          groupPaging: true
      }, 'remote operations set correct');
  });
  
  QUnit.test("No redefine user remoteOperations when remote groupPaging is true", function(assert) {
      //arrange
      this.applyOptions({
          remoteOperations: {
              groupPaging: true,
              sorting: false,
              summary: false
          },
          dataSource: [
              { name: 'Alex', age: 20 }
          ]
      });
  
      //actt
      this.dataController._refreshDataSource();
  
      //assert
      assert.deepEqual(this.dataController.dataSource().remoteOperations(), {
          filtering: true,
          sorting: false,
          paging: true,
          grouping: true,
          summary: false,
          groupPaging: true
      }, 'remote operations set correct');
  });
  
  QUnit.test("Remote operations with CustomStore", function(assert) {
      //arrange
      this.applyOptions({
          remoteOperations: "auto",
          dataSource: {
              load: function() { }
          }
      });
  
      //actt
      this.dataController._refreshDataSource();
  
      //assert
      assert.deepEqual(this.dataController.dataSource().remoteOperations(), {}, "remote operations set correct");
  });
  
  QUnit.test("Set remoteOperations option to true", function(assert) {
      //arrange
      this.applyOptions({
          remoteOperations: true,
          dataSource: [
              { name: 'Alex', age: 20, phone: '55-55-55' },
              { name: 'Dan', age: 30, phone: '98-75-21' }
          ]
      });
  
      //actt
      this.dataController._refreshDataSource();
  
      //assert
      assert.deepEqual(this.dataController.dataSource().remoteOperations(), { filtering: true, sorting: true, paging: true, grouping: true, summary: true }, "remote operations set correct");
  });
  
  QUnit.module("No dataSource", { beforeEach: setupModule, afterEach: teardownModule });
  
  QUnit.test("getters", function(assert) {
      assert.strictEqual(this.dataController.items().length, 0);
      assert.strictEqual(this.dataController.totalItemsCount(), 0);
      assert.strictEqual(this.dataController.pageCount(), 1);
      assert.strictEqual(this.dataController.pageIndex(), 0);
      assert.strictEqual(this.dataController.pageSize(), 0);
      assert.strictEqual(this.dataController.isLoading(), false);
  });
  
  QUnit.test("change pageIndex", function(assert) {
      this.applyOptions({});
      this.dataController.pageIndex(2);
      assert.strictEqual(this.dataController.pageIndex(), 0);
  });
  
  QUnit.test("change pageSize", function(assert) {
      this.applyOptions({});
      this.dataController.pageSize(100);
      assert.strictEqual(this.dataController.pageSize(), 0);
  });
  
  QUnit.test("refresh", function(assert) {
      this.applyOptions({});
      this.dataController.refresh();
      assert.ok(1);
  });
  
  QUnit.test("begin/end customLoading", function(assert) {
      this.applyOptions({});
      this.dataController.beginCustomLoading();
      assert.ok(this.dataController.isLoading());
      this.dataController.endCustomLoading();
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.module("Loading", { beforeEach: setupModule, afterEach: teardownModule });
  
  QUnit.test("LoadingChanged event on one loading", function(assert) {
      var that = this,
          loadingStates = [],
          dataSource = createDataSource([{ id: 1 }, { id: 2 }, { id: 3 }], {}, {
              pageSize: 2
          });
      that.dataController.setDataSource(dataSource);
      dataSource.load();
  
      that.dataController.loadingChanged.add(function(isLoading) {
          loadingStates.push(isLoading);
      });
  
      assert.deepEqual(loadingStates, []);
  
      //act
      that.dataController.pageIndex(1);
  
      //assert
      assert.deepEqual(loadingStates, [true, false]);
  });
  
  QUnit.test("isLoading for loaded dataSource", function(assert) {
      var arrayStore = new ArrayStore([]);
      var dataSource = new DataSource({
          store: arrayStore,
          _preferSync: true
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //assert
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("begin custom loading", function(assert) {
      var loadingStates = [];
      var dataSource = createDataSource([]);
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.dataController.loadingChanged.add(function(isLoading) {
          loadingStates.push(isLoading);
      });
  
      assert.deepEqual(loadingStates, []);
  
      //act
      this.dataController.beginCustomLoading();
  
      //assert
      assert.ok(this.dataController.isLoading());
      assert.deepEqual(loadingStates, [true]);
  });
  
  QUnit.test("end custom loading", function(assert) {
      var loadingStates = [];
      var dataSource = createDataSource([]);
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.dataController.loadingChanged.add(function(isLoading) {
          loadingStates.push(isLoading);
      });
  
      this.dataController.beginCustomLoading();
  
      //act
      this.dataController.endCustomLoading();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.deepEqual(loadingStates, [true, false]);
  });
  
  //T179346
  QUnit.test("Loading for lookup column", function(assert) {
      var lookupDeferred = $.Deferred();
      var loadingStates = [];
      var arrayStore = new ArrayStore([]);
      var dataSource = new DataSource({
          store: arrayStore,
          _preferSync: true
      });
  
      this.applyOptions({
          columns: [{
              dataField: 'testId',
              lookup: {
                  valueExpr: "test",
                  dataSource: {
                      load: function() {
                          return lookupDeferred;
                      }
                  }
              }
          }]
      });
  
      //act
      this.dataController.loadingChanged.add(function(isLoading) {
          loadingStates.push(isLoading);
      });
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //assert
      assert.ok(this.dataController.isLoading(), 'isLoading is true when lookup dataSource is not loaded');
      assert.deepEqual(loadingStates, [true, false, true], 'loading states before lookup dataSource is loaded');
  
      //act
      lookupDeferred.resolve([]);
  
      //assert
      assert.deepEqual(loadingStates, [true, false, true, false], 'loading states when lookup dataSource is loaded');
      assert.ok(!this.dataController.isLoading(), 'isLoading when lookup dataSource is loaded');
  });
  
  QUnit.module("Parsing values", { beforeEach: setupModule, afterEach: teardownModule });
  
  QUnit.test("Null value for dateTime dataType", function(assert) {
      var array = [
          { name: 'Alex', birthday: null }
      ];
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.applyOptions({
          columns: ['name', { dataField: "birthday", dataType: 'date', format: 'shortDate' }]
      });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', null]);
  });
  
  QUnit.test("value from calculateCellValue", function(assert) {
      var array = [
          { firstName: 'Alex', secondName: 'Ivanov' },
          { firstName: 'Boris', secondName: 'Sidorov' }
      ];
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.applyOptions({
          columns: [{ calculateCellValue: function(data) { return data.firstName + ' ' + data.secondName; } }]
      });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      assert.equal(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].values, ['Alex Ivanov']);
      assert.deepEqual(this.dataController.items()[1].values, ['Boris Sidorov']);
  });
  
  var setupPagingModule = function() {
      setupModule.apply(this);
  
      this.array = [
          { name: 'Alex', pay: 215 },
          { name: 'Dan1', pay: 151 },
          { name: 'Dan2', pay: 152 },
          { name: 'Dan3', pay: 153 },
          { name: 'Dan4', pay: 154 },
          { name: 'Dan5', pay: 155 },
          { name: 'Dan6', pay: 156 }
      ];
      var dataSource = createDataSource(this.array, { key: 'name' }, {
          pageSize: 5,
          paginate: true
      });
  
      this.dataSource = dataSource;
  };
  
  var teardownPagingModule = function() {
      teardownModule.apply(this);
  };
  
  QUnit.module("Paging", { beforeEach: setupPagingModule, afterEach: teardownPagingModule });
  
  QUnit.test("PagesCount, TotalCount, Rows after initialization", function(assert) {
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      assert.equal(this.dataController.items().length, 5);
      assert.equal(this.dataController.totalCount(), 7);
      assert.equal(this.dataController.pageCount(), 2);
      assert.equal(this.dataController.pageIndex(), 0);
      assert.ok(this.dataController.hasKnownLastPage());
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 215]);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Alex', pay: 215 });
  });
  
  QUnit.test("PagesCount after filter dataSource", function(assert) {
      var changedCount = 0;
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      this.dataController.changed.add(function() {
          changedCount++;
      });
  
      //act
      this.dataSource.filter(['name', 'Dan3']);
      this.dataSource.reload(true);
  
      //assert
      assert.equal(changedCount, 1);
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.totalCount(), 1);
      assert.equal(this.dataController.pageCount(), 1);
      assert.equal(this.dataController.pageIndex(), 0);
      assert.deepEqual(this.dataController.items()[0].values, ['Dan3', 153]);
  });
  
  QUnit.test("Change pageIndex", function(assert) {
      //arrange
      var countCallPageChanged = 0;
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      this.dataController.pageChanged.add(function() {
          countCallPageChanged++;
      });
  
      //act
      this.dataController.pageIndex(1);
  
      assert.equal(this.dataController.items().length, 2);
      assert.equal(this.dataController.pageIndex(), 1);
      assert.deepEqual(this.dataController.items()[0].values, ['Dan5', 155]);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Dan5', pay: 155 });
  
      //T262949
      assert.equal(countCallPageChanged, 1, "pageChanged is called");
  });
  
  QUnit.test("get pageIndex after change dataSource pageIndex", function(assert) {
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      this.dataSource.pageIndex(1);
      this.dataSource.reload(true);
  
      assert.equal(this.dataController.items().length, 2);
      assert.equal(this.dataController.pageIndex(), 1);
  });
  
  //B233043
  QUnit.test("change pageIndex to greater then pageCount", function(assert) {
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      this.dataController.pageIndex(5);
  
      assert.equal(this.dataController.items().length, 2);
      assert.equal(this.dataController.pageIndex(), 1);
  });
  
  QUnit.test("Change pageSize", function(assert) {
      //arrange
      var countCallPageChanged = 0;
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      this.dataController.pageIndex(1);
  
      this.dataController.pageChanged.add(function() {
          countCallPageChanged++;
      });
  
      //act
      this.dataController.pageSize(3);
  
      //assert
      assert.equal(this.dataController.items().length, 3);
      assert.equal(this.dataController.pageCount(), 3);
      assert.equal(this.dataController.pageIndex(), 0);
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 215]);
  
      //T262949
      assert.equal(countCallPageChanged, 1, "count call pageChanged");
  });
  
  QUnit.test("Change dataSource after change pageSize", function(assert) {
      this.options.dataSource = this.array;
      this.options.paging = { enabled: true, pageSize: 5 };
  
      this.dataController.optionChanged({ name: 'dataSource' });
  
      //act
      this.dataController.pageSize(2);
  
      //assert
      assert.equal(this.dataController.pageSize(), 2);
      assert.equal(this.dataController.pageCount(), 4);
  
      //act
      this.dataController.optionChanged({ name: 'dataSource' });
  
      //assert
      assert.equal(this.dataController.pageSize(), 2);
      assert.equal(this.dataController.pageCount(), 4);
      assert.equal(this.option('paging.pageSize'), 2);
  });
  
  //T310271
  QUnit.test("Change dataSource after change pageIndex", function(assert) {
      this.options.dataSource = this.array;
      this.options.paging = { enabled: true, pageSize: 5 };
  
      this.dataController.optionChanged({ name: 'dataSource' });
  
      //act
      this.dataController.pageIndex(1);
  
      //assert
      assert.equal(this.dataController.pageIndex(), 1);
      assert.equal(this.dataController.pageCount(), 2);
  
      //act
      this.dataController.optionChanged({ name: 'dataSource' });
  
      //assert
      assert.equal(this.dataController.pageIndex(), 1);
      assert.equal(this.dataController.pageCount(), 2);
      assert.equal(this.option('paging.pageIndex'), 1);
  });
  
  
  QUnit.test("Rise changed on set pageSize", function(assert) {
      var changedCount = 0;
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      this.dataController.pageSize(10);
  
      this.dataController.changed.add(function(controller) {
          changedCount++;
      });
  
      //act
      this.dataController.pageSize(20);
  
      //assert
      assert.equal(changedCount, 1, 'on change pageSize');
  });
  
  QUnit.test("Rise changed on set pageSize with changing pageCount", function(assert) {
      var changedCount = 0;
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      this.dataController.changed.add(function(controller) {
          changedCount++;
      });
  
      //act
      this.dataController.pageSize(3);
  
      //assert
      assert.equal(changedCount, 1, 'one on change pageSize');
  });
  
  //B239351
  QUnit.test("Rise changed on set pageIndex", function(assert) {
      var changedCallCount = 0;
      var dataController = this.dataController;
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      dataController.changed.add(function() {
          changedCallCount++;
      });
      dataController.pageIndex(1);
      this.clock.tick();
      assert.equal(dataController.pageIndex(), 1);
      assert.equal(changedCallCount, 1);
  });
  
  
  QUnit.test("Not Rise changed on get pageIndex", function(assert) {
      var changedCount = 0;
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      this.dataController.changed.add(function() {
          changedCount++;
      });
      assert.equal(this.dataController.pageIndex(), 0);
      assert.equal(changedCount, 0);
  });
  
  QUnit.test("update pageCount after insert", function(assert) {
      var changedCount = 0;
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      this.dataController.changed.add(function() {
          changedCount++;
      });
  
      this.dataSource.store().insert({ name: 'Dan7', pay: 163 });
      this.dataSource.store().insert({ name: 'Dan8', pay: 164 });
      this.dataSource.store().insert({ name: 'Dan9', pay: 165 });
      this.dataSource.store().insert({ name: 'Dan10', pay: 166 });
      this.dataSource.reload(true);
  
      assert.equal(this.dataController.pageIndex(), 0);
      assert.equal(this.dataController.totalCount(), 11);
      assert.equal(this.dataController.pageCount(), 3);
      assert.equal(changedCount, 1, 'changed raise after reload');
  });
  
  //T202716
  QUnit.test("initialize pageIndex, pageSize from paging options when dataSource is DataSource instance", function(assert) {
      this.options.dataSource = this.dataSource;
      this.options.paging = {
          enabled: true,
          pageSize: 3,
          pageIndex: 1
      };
  
      this.dataController._refreshDataSource();
  
      assert.equal(this.dataController.pageIndex(), 1);
      assert.equal(this.dataController.pageCount(), 3);
      assert.ok(this.dataController.hasKnownLastPage());
      assert.equal(this.dataController.totalCount(), 7);
      assert.equal(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].values, ['Dan3', 153]);
  });
  
  QUnit.test("Page size of data source is not changed for old value_T242652", function(assert) {
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      //act
      var dataSourceChangedCounter = 0;
  
      this.dataSource.on("changed", function() {
          dataSourceChangedCounter++;
      });
  
      this.dataController.pageSize(1);
      this.dataController.pageSize(1);
  
      //assert
      assert.equal(dataSourceChangedCounter, 1);
  });
  
  var setupVirtualScrollingModule = function() {
      this.options = {
          scrolling: { mode: 'virtual' },
          pager: { visible: 'auto' }
      };
      setupModule.apply(this);
  
      var i, array = [];
  
      for(i = 0; i < 1000; i++) {
          array.push({
              id: i,
              value: 'value' + i.toString()
          });
      }
  
      var dataSource = createDataSource(array, { key: 'id' }, {
          pageSize: 20,
          paginate: true
      });
  
      this.array = array;
  
      this.dataController.setDataSource(dataSource);
      this.dataController.viewportSize(10);
      dataSource.load();
      this.dataSource = dataSource;
  };
  
  var teardownVirtualScrollingModule = function() {
      teardownModule.apply(this);
  };
  
  QUnit.module("Virtual scrolling", { beforeEach: setupVirtualScrollingModule, afterEach: teardownVirtualScrollingModule });
  
  QUnit.test("virtual items return null when no virtual scrolling", function(assert) {
      var dataSource = createDataSource(this.array, { key: 'id' }, {
          pageSize: 20,
          paginate: true,
          userPageSize: 20
      });
      this.applyOptions({
          scrolling: { mode: 'standard' }
      });
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      assert.ok(!this.dataController.virtualItemsCount());
  });
  
  QUnit.test("virtual items when virtual scrolling enabled", function(assert) {
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
  });
  
  QUnit.test("virtual items at begin", function(assert) {
      this.dataController.setViewportItemIndex(0);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 0,
          end: 960
      });
      assert.equal(this.dataController.items().length, 40);
  });
  
  QUnit.test("virtual items at end", function(assert) {
      this.dataController.setViewportItemIndex(985);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 960,
          end: 0
      });
      assert.equal(this.dataController.items().length, 40);
  });
  
  QUnit.test("virtual items after small scrolling at beginning", function(assert) {
      this.dataController.setViewportItemIndex(1);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 0,
          end: 960
      });
      assert.equal(this.dataController.items().length, 40);
  });
  
  QUnit.test("virtual items before page center", function(assert) {
      this.dataController.setViewportItemIndex(9);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 0,
          end: 960
      });
      assert.equal(this.dataController.items().length, 40);
      assert.strictEqual(this.dataController.pageIndex(), 0);
  });
  
  QUnit.test("virtual items after page center", function(assert) {
      this.dataController.setViewportItemIndex(11);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 0,
          end: 960
      });
      assert.equal(this.dataController.items().length, 40);
      assert.strictEqual(this.dataController.pageIndex(), 0);
  });
  
  QUnit.test("virtual items before page end", function(assert) {
      this.dataController.setViewportItemIndex(19);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 0,
          end: 960
      });
      assert.equal(this.dataController.items().length, 40);
      assert.strictEqual(this.dataController.pageIndex(), 0);
  });
  
  QUnit.test("virtual items after page end", function(assert) {
      this.dataController.setViewportItemIndex(21);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 0,
          end: 940
      });
      assert.equal(this.dataController.items().length, 60);
      assert.strictEqual(this.dataController.pageIndex(), 1);
  });
  
  
  QUnit.test("virtual items after change current page to next", function(assert) {
      this.dataController.setViewportItemIndex(25);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.ok(virtualItemsCount);
      assert.deepEqual(virtualItemsCount, {
          begin: 0,
          end: 940
      });
      assert.equal(this.dataController.items().length, 60);
      assert.strictEqual(this.dataController.pageIndex(), 1);
  });
  
  QUnit.test("virtual items after change current page to previous", function(assert) {
      this.dataController.setViewportItemIndex(25);
      assert.strictEqual(this.dataController.pageIndex(), 1);
  
      this.dataController.setViewportItemIndex(5);
  
      assert.strictEqual(this.dataController.pageIndex(), 0);
  
      assert.equal(this.dataController.items().length, 60);
      assert.deepEqual(this.dataController.virtualItemsCount(), {
          begin: 0,
          end: 940
      });
  });
  
  QUnit.test("virtual items after change current page to far", function(assert) {
      this.dataController.setViewportItemIndex(400);
      var virtualItemsCount = this.dataController.virtualItemsCount();
      assert.deepEqual(virtualItemsCount, {
          begin: 400,
          end: 560
      });
      assert.equal(this.dataController.items().length, 40);
      assert.strictEqual(this.dataController.pageIndex(), 20);
  });
  
  QUnit.test("virtual items on end when last page size less than half page size", function(assert) {
      var dataController = this.dataController;
      dataController.store().insert({ id: 1001, value: 'value1001' });
      dataController.refresh();
  
      dataController.setViewportItemIndex(999);
      assert.strictEqual(this.dataController.pageIndex(), 49);
  
      this.clock.tick();
  
      assert.strictEqual(dataController.items().length, 21, "items");
  
      //act
      var virtualItemsCount = dataController.virtualItemsCount();
  
      //assert
      assert.strictEqual(virtualItemsCount.begin, 980);
      assert.strictEqual(virtualItemsCount.end, 0);
  });
  
  //B233350
  QUnit.test("virtual items on end when visibleRowsCount < pageSize and last page size greater than half page size", function(assert) {
      var dataController = this.dataController;
      dataController._dataSource.store().remove(999);
      dataController.refresh();
      dataController.setViewportItemIndex(998);
      assert.strictEqual(this.dataController.pageIndex(), 49);
  
      this.clock.tick();
  
      assert.strictEqual(dataController.items().length, 39);
  
      //act
      var virtualItemsCount = dataController.virtualItemsCount();
  
      //assert
      assert.strictEqual(virtualItemsCount.begin, 960);
      assert.strictEqual(virtualItemsCount.end, 0);
  });
  
  QUnit.test("virtual items on setViewportItemIndex at end", function(assert) {
      //act
      this.dataController.setViewportItemIndex(990);
  
      //assert
  
      assert.strictEqual(this.dataController.items().length, 40);
      assert.strictEqual(this.dataController.virtualItemsCount().end, 0);
  });
  
  QUnit.test("getAllRowsCount for virtual scrolling", function(assert) {
      assert.strictEqual(this.dataController.totalItemsCount(), 1000);
  });
  
  //T308521
  QUnit.test("change sorting in onContentReady", function(assert) {
      //arrange
      var that = this,
          countCallChanged = 0,
          countCallDataSourceChanged = 0,
          dataSource = that.dataController.dataSource();
  
      that.dataController.changed.add(function() {
          that.columnOption("id", "sortOrder", "desc");
          countCallChanged++;
      });
  
      dataSource.changed.add(function() {
          countCallDataSourceChanged++;
      });
  
      //act
      dataSource.reload(true);
  
      //arrange
      assert.equal(that.dataController.items()[0].key, 999, "sorting is applied");
      assert.equal(countCallChanged, 3, "count call changed of the dataController");
      assert.equal(countCallDataSourceChanged, 3, "count call changed of the dataSource");
  });
  
  //=================================
  //scrollingDataSource tests
  
  var TEN_NUMBERS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  
  QUnit.module("Virtual scrolling (ScrollingDataSource)", {
      beforeEach: function() {
  
          this.options = {
              scrolling: { mode: 'virtual' },
              pager: { visible: 'auto' },
              paging: {},
              remoteOperations: { filtering: true, sorting: true, paging: true },
              grouping: { autoExpandAll: true }
          };
          setupModule.apply(this);
  
          this.setupDataSource = function(options) {
              this.options.paging.pageSize = options.pageSize;
              this.dataSource = createDataSource(options.data || TEN_NUMBERS, {}, $.extend({ paginate: true }, options));
              this.dataController.setDataSource(this.dataSource);
              this.dataSource.load();
              this.dataController.viewportSize(2);
          };
  
          this.getDataItems = function(items) {
              var dataItems = [];
  
              items = items || this.dataController.items();
  
              $.each(items, function() {
                  dataItems.push(this.data);
              });
              return dataItems;
          };
  
          this.clock = sinon.useFakeTimers();
      }, afterEach: function() {
          teardownModule.call(this);
          this.clock.restore();
      }
  });
  
  QUnit.test("load first page", function(assert) {
      //act
      this.setupDataSource({
          pageSize: 3
      });
  
      //assert
      assert.deepEqual(this.getDataItems(), [1, 2, 3, 4, 5, 6], "two pages are loaded");
      assert.ok(this.dataController.isLoaded());
  });
  
  QUnit.test("preload next page after change viewport item index", function(assert) {
      this.setupDataSource({
          pageSize: 3
      });
  
      var changedArgs = [];
  
      this.dataController.viewportSize(2);
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      //act
      this.dataController.setViewportItemIndex(3);
  
      //assert
      assert.equal(changedArgs.length, 1);
      assert.equal(changedArgs[0].changeType, 'append');
      assert.deepEqual(this.getDataItems(changedArgs[0].items), [7, 8, 9]);
      assert.deepEqual(this.getDataItems(), [1, 2, 3, 4, 5, 6, 7, 8, 9]);
      assert.ok(this.dataController.isLoaded());
  });
  
  //T103382
  QUnit.test("moving to loaded page rise changed event with changeType pageIndex", function(assert) {
      var changedCallsCount = 0,
          changedArgs;
  
      this.setupDataSource({
          pageSize: 3
      });
  
      this.dataController.viewportSize(2);
      this.dataController.setViewportItemIndex(0);
      this.dataController.setViewportItemIndex(3);
  
      assert.equal(this.dataController.pageIndex(), 1);
  
      this.dataController.changed.add(function(e) {
          changedCallsCount++;
          changedArgs = e;
      });
  
      //act
      this.dataController.setViewportItemIndex(0);
  
      //assert
      assert.equal(changedCallsCount, 1);
      assert.equal(changedArgs.changeType, 'pageIndex');
      assert.equal(this.dataController.pageIndex(), 0, 'pageIndex');
      assert.deepEqual(this.getDataItems(), [1, 2, 3, 4, 5, 6, 7, 8, 9]);
  });
  
  QUnit.test("load pages after change viewport item index to far", function(assert) {
      var changedArgs = [];
  
      this.options.loadingTimeout = 0;
      this.setupDataSource({
          pageSize: 2
      });
      this.dataController.viewportSize(2);
      this.clock.tick();
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      this.dataController.setViewportItemIndex(7);
      //act
      this.clock.callTimer(this.clock.firstTimerInRange());
      //assert
      assert.equal(changedArgs.length, 1);
      assert.equal(changedArgs[0].changeType, 'refresh');
      assert.deepEqual(this.getDataItems(changedArgs[0].items), [7, 8]);
      assert.deepEqual(this.getDataItems(), [7, 8]);
      assert.ok(this.dataController.isLoaded());
  
  
      //act
      this.clock.callTimer(this.clock.firstTimerInRange());
  
      //assert
      assert.equal(changedArgs.length, 2);
      assert.deepEqual(changedArgs[1].changeType, 'append');
      assert.deepEqual(this.getDataItems(changedArgs[1].items), [9, 10]);
      assert.deepEqual(this.getDataItems(), [7, 8, 9, 10]);
      assert.ok(this.dataController.isLoaded());
  });
  
  //T103389
  QUnit.test("load several pages when pageSize less then viewportSize", function(assert) {
      var changedArgs = [];
  
      this.options.loadingTimeout = 0;
      this.setupDataSource({
          pageSize: 2
      });
      this.dataController.viewportSize(3);
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
      //act
      this.clock.tick();
  
      //assert
      assert.equal(changedArgs.length, 3);
      assert.deepEqual(changedArgs[0].changeType, 'refresh');
      assert.deepEqual(this.getDataItems(changedArgs[0].items), [1, 2]);
      assert.deepEqual(changedArgs[1].changeType, 'append');
      assert.deepEqual(this.getDataItems(changedArgs[1].items), [3, 4]);
      assert.deepEqual(changedArgs[2].changeType, 'append');
      assert.deepEqual(this.getDataItems(changedArgs[2].items), [5, 6]);
      assert.deepEqual(this.getDataItems(), [1, 2, 3, 4, 5, 6]);
      assert.ok(this.dataController.isLoaded());
  });
  
  //T103389
  QUnit.test("load several pages when pageSize less then viewportSize and preload enabled", function(assert) {
      var changedArgs = [];
  
      this.options.loadingTimeout = 0;
      this.options.scrolling.preloadEnabled = true;
      this.setupDataSource({
          pageSize: 2
      });
      this.dataController.viewportSize(3);
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
      //act
      this.clock.tick();
  
      //assert
      assert.equal(changedArgs.length, 4);
      assert.deepEqual(this.getDataItems(), [1, 2, 3, 4, 5, 6, 7, 8]);
      assert.ok(this.dataController.isLoaded());
  });
  
  //T120733
  QUnit.test("reload pages after change viewport item index to far", function(assert) {
      var changedArgs = [],
          virtualItems = [];
  
      this.options.loadingTimeout = 0;
      this.setupDataSource({
          pageSize: 2
      });
  
      var dataController = this.dataController;
  
  
      this.clock.tick();
      dataController.setViewportItemIndex(7);
      this.clock.tick();
      dataController.changed.add(function(e) {
          changedArgs.push(e);
          virtualItems.push(dataController.virtualItemsCount());
      });
  
      //act
      dataController.reload(true);
      this.clock.callTimer(this.clock.firstTimerInRange());
  
      //assert
      assert.deepEqual(changedArgs, []);
      assert.deepEqual(this.getDataItems(), [7, 8, 9, 10]);
      assert.ok(dataController.isLoaded());
  
      //act
      this.clock.callTimer(this.clock.firstTimerInRange());
  
      //assert
      assert.equal(changedArgs.length, 2);
      assert.equal(changedArgs[0].changeType, 'refresh');
      assert.deepEqual(this.getDataItems(changedArgs[0].items), [7, 8]);
      assert.equal(changedArgs[1].changeType, 'append');
      assert.deepEqual(this.getDataItems(changedArgs[1].items), [9, 10]);
      assert.deepEqual(virtualItems, [{ begin: 6, end: 2 }, { begin: 6, end: 0 }]);
      assert.deepEqual(this.getDataItems(), [7, 8, 9, 10]);
      assert.ok(dataController.isLoaded());
  
  });
  
  QUnit.test("preload pages after change viewport item index", function(assert) {
      var changedArgs = [];
  
      this.options.scrolling.preloadEnabled = true;
  
      this.setupDataSource({
          pageSize: 3
      });
  
      var dataController = this.dataController;
  
      dataController.viewportSize(2);
      dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      //act
      dataController.setViewportItemIndex(3);
  
      //assert
      assert.equal(changedArgs.length, 1);
      assert.deepEqual(changedArgs[0].changeType, 'append');
      assert.deepEqual(this.getDataItems(changedArgs[0].items), [10]);
      assert.deepEqual(this.getDataItems(), TEN_NUMBERS);
      assert.ok(dataController.isLoaded());
  });
  
  //T444542
  QUnit.test("Preload previous page after change viewport item index when preloadEnabled option is true", function(assert) {
      //arrange
      var changedArgs = [];
  
      this.options.scrolling.preloadEnabled = true;
      this.setupDataSource({
          pageSize: 1
      });
  
      var dataController = this.dataController;
  
      dataController.viewportSize(1);
      dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      //act
      dataController.setViewportItemIndex(4);
  
      //assert
      assert.equal(changedArgs.length, 4, "load count");
      assert.equal(changedArgs[0].changeType, "refresh", "load current page");
      assert.equal(changedArgs[1].changeType, "prepend", "load previous page");
      assert.equal(changedArgs[2].changeType, "append", "load next page");
      assert.equal(changedArgs[3].changeType, "append", "load next page");
      assert.deepEqual(this.getDataItems(), [4, 5, 6, 7], "items");
      assert.ok(dataController.isLoaded(), "data is loaded");
  });
  
  QUnit.test("update loading on reload", function(assert) {
      var finalized;
  
      this.options.loadingTimeout = 0;
      this.setupDataSource({
          pageSize: 3
      });
  
      var dataController = this.dataController;
  
      dataController.load().done(function() {
          var isLoadingByEvent;
          dataController.loadingChanged.add(function(isLoading) {
              isLoadingByEvent = isLoading;
              if(isLoading) {
                  assert.ok(!dataController.isLoaded(), 'isLoaded on reload in loadingChanged event');
              }
          });
  
          //act
          dataController.reload(true);
  
          //assert
          assert.ok(!dataController.isLoaded());
          assert.ok(dataController.isLoading());
          assert.ok(isLoadingByEvent);
          finalized = true;
      });
  
      this.clock.tick();
      assert.ok(finalized);
  });
  
  //T152307
  QUnit.test("update loading on reload when error occurred", function(assert) {
      var finalized;
      var loadResult;
  
      var clock = this.clock;
  
      this.options.loadingTimeout = 0;
  
  
      this.dataSource = new DataSource({
          load: function() {
              return loadResult || [];
          }
      });
      this.dataController.setDataSource(this.dataSource);
  
      var dataController = this.dataController;
  
      dataController.load().done(function() {
          var isLoadingByEvent;
          dataController.loadingChanged.add(function(isLoading) {
              isLoadingByEvent = isLoading;
              if(isLoading) {
                  assert.ok(!dataController.isLoaded(), 'isLoaded on reload in loadingChanged event');
              }
          });
  
          var changedArgs = [];
  
          dataController.changed.add(function(e) {
              changedArgs.push(e);
          });
  
          loadResult = $.Deferred().reject('user error');
  
          //act
          dataController.reload(true);
  
          //assert
          assert.ok(!dataController.isLoaded(), 'isLoaded');
          assert.ok(dataController.isLoading(), 'isLoading');
          assert.ok(isLoadingByEvent, 'isLoading by event');
  
          clock.tick();
  
          assert.ok(!dataController.isLoaded(), 'isLoaded after error');
          assert.ok(!dataController.isLoading(), 'isLoading after error');
          assert.ok(!isLoadingByEvent, 'isLoading by event after error');
  
          assert.equal(changedArgs.length, 1);
          assert.equal(changedArgs[0].changeType, 'loadError');
          assert.equal(changedArgs[0].error.message, 'user error');
  
          finalized = true;
      });
  
      clock.tick();
      assert.ok(finalized);
  });
  
  //T103219
  QUnit.test("loadingChanged must be before changed", function(assert) {
      //arrange
      var events = [];
  
      this.setupDataSource({
          asyncLoadEnabled: true,
          pageSize: 3
      });
  
      var dataController = this.dataController;
  
  
      dataController.load();
      this.clock.tick();
  
      dataController.loadingChanged.add(function() {
          events.push('loadingChanged');
      });
  
      dataController.changed.add(function() {
          events.push('changed');
      });
  
      //act
      dataController.reload(true);
      this.clock.tick();
  
      //assert
      assert.deepEqual(events, ['loadingChanged', 'loadingChanged', 'changed', 'changed']);
  });
  
  //B252337
  QUnit.test("update loading when grouping", function(assert) {
      var array = $.map(TEN_NUMBERS, function(value) { return { value: value }; });
  
      this.setupDataSource({
          data: array,
          group: 'value',
          pageSize: 5
      });
      this.dataSource.load();
  
      var dataController = this.dataController;
  
      var isLoadingByEvent;
      dataController.loadingChanged.add(function(isLoading) {
          isLoadingByEvent = isLoading;
      });
  
      dataController.viewportSize(5);
      dataController.setViewportItemIndex(0);
  
      //act
      dataController.setViewportItemIndex(9);
  
      var items = [];
      $.each(TEN_NUMBERS, function(index, value) {
          items.push({ key: value, items: [{ value: value }] });
          items.push({ value: value });
      });
  
      //assert
      assert.deepEqual(this.getDataItems(), items);
      assert.equal(dataController.itemsCount(), 20);
      assert.ok(dataController.isLoaded());
      assert.ok(!dataController.isLoading(), 'loading completed');
      assert.ok(!isLoadingByEvent, 'loading completed');
  });
  
  //B252337
  QUnit.test("update loading when dataSource length less then pageSize", function(assert) {
      this.setupDataSource({
          pageSize: 20
      });
      this.dataSource.load();
  
      var dataController = this.dataController;
  
  
      dataController.viewportSize(15);
  
      var isLoadingByEvent;
      dataController.loadingChanged.add(function(isLoading) {
          isLoadingByEvent = isLoading;
      });
  
      //act
      dataController.load();
  
  
      //assert
      assert.deepEqual(this.getDataItems(), TEN_NUMBERS);
      assert.equal(dataController.itemsCount(), 10);
      assert.ok(dataController.isLoaded());
      assert.ok(!dataController.isLoading(), 'loading completed');
      assert.ok(!isLoadingByEvent, 'loading completed');
  });
  
  //B254273
  QUnit.test('Loading state when dataSource loading completed and itemsCount less than viewportSize', function(assert) {
      var array = $.map(TEN_NUMBERS, function(value) { return { value: value }; });
  
      this.setupDataSource({
          data: array,
          group: 'value'
      });
  
      var dataController = this.dataController;
  
  
      dataController.viewportSize(25);
      dataController.setViewportItemIndex(0);
  
      //act
      dataController.load();
  
      //assert
      assert.ok(!dataController.isLoading(), 'loading completed');
  });
  
  //B254146
  QUnit.test("changeRowExpand on second page when virtual scrolling enabled", function(assert) {
      var array = $.map(TEN_NUMBERS, function(value) { return { value: value }; });
  
      this.setupDataSource({
          data: array,
          group: 'value',
          pageSize: 3
      });
  
      var dataController = this.dataController;
  
  
      dataController.viewportSize(3);
  
      //act
      dataController.setViewportItemIndex(0);
  
      dataController.setViewportItemIndex(4);
  
      dataController.changeRowExpand([4]);
  
      assert.equal(dataController.pageIndex(), 1, 'pageIndex'); // must be 1
      assert.deepEqual(dataController.items()[0].data, { collapsedItems: [], key: 4, items: null, count: 1 }, 'item 4 collapsed');
  });
  
  //B254113
  QUnit.test("Start loading after changeRowExpand when virtual scrolling enabled", function(assert) {
      var array = $.map(TEN_NUMBERS, function(value) { return { value: value }; });
      var isLoading = false;
  
      this.options.loadingTimeout = 0;
  
      this.setupDataSource({
          data: array,
          group: 'value',
          pageSize: 3
      });
  
      var dataController = this.dataController;
  
  
      dataController.loadingChanged.add(function(state) {
          isLoading = state;
      });
  
      dataController.load();
      dataController.viewportSize(3);
      this.clock.tick();
  
      //assert
      assert.ok(!isLoading, 'not loading');
  
      //act
      dataController.setViewportItemIndex(0);
  
      dataController.changeRowExpand([0]);
  
      assert.ok(isLoading, 'loading started');
      this.clock.tick();
      assert.ok(!isLoading, 'loading ended');
  });
  
  //T122785
  QUnit.test("not update pageSize on viewportSize", function(assert) {
      var changedArgs = [];
  
      this.setupDataSource({
          data: TEN_NUMBERS.concat(TEN_NUMBERS)
      });
  
      var dataController = this.dataController;
  
  
      dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      //act
      dataController.viewportSize(2);
  
      //assert
      assert.equal(changedArgs.length, 0);
      assert.deepEqual(this.getDataItems().length, 20);
      assert.equal(dataController.pageSize(), 20);
      assert.ok(dataController.isLoaded());
  });
  
  
  QUnit.module("Infinite scrolling", {
      beforeEach: function() {
          setupModule.apply(this);
  
          var i, array = [];
  
          for(i = 0; i < 50; i++) {
              array.push({
                  id: i,
                  value: 'value' + i.toString()
              });
          }
  
          var dataSource = createDataSource(array, { key: 'id' }, {
              pageSize: 20,
              paginate: true,
              requireTotalCount: false,
              scrolling: { mode: 'infinite' }
          });
  
          this.applyOptions({
              scrolling: { mode: 'infinite' },
              pager: { visible: 'auto' }
          });
          this.dataController.setDataSource(dataSource);
          this.dataController.viewportSize(10);
          dataSource.load();
          this.dataSource = dataSource;
      },
      afterEach: teardownModule
  });
  
  QUnit.test("setViewportItemIndex to begin current page not load next page", function(assert) {
      //act
      this.dataController.setViewportItemIndex(1);
  
      //assert
      assert.strictEqual(this.dataController.pageIndex(), 0);
      assert.strictEqual(this.dataController.hasKnownLastPage(), false);
      assert.strictEqual(this.dataController.items().length, 20);
  });
  
  QUnit.test("setViewportItemIndex to end current page load next page", function(assert) {
      //act
      this.dataController.setViewportItemIndex(10);
  
      //assert
      assert.strictEqual(this.dataController.pageIndex(), 1);
      assert.strictEqual(this.dataController.hasKnownLastPage(), false);
      assert.strictEqual(this.dataController.items().length, 40);
  });
  
  //T193217
  QUnit.test("setViewportItemIndex to end current page several times load next page one time", function(assert) {
      var loadingCount = 0;
      this.dataController._dataSource.customizeStoreLoadOptions.add(function() {
          loadingCount++;
      });
      //act
      this.options.loadingTimeout = 0;
      this.dataController.setViewportItemIndex(10);
      this.dataController.setViewportItemIndex(10);
  
      this.clock.tick();
  
      //assert
      assert.strictEqual(loadingCount, 1);
      assert.strictEqual(this.dataController.pageIndex(), 1);
      assert.strictEqual(this.dataController.hasKnownLastPage(), false);
      assert.strictEqual(this.dataController.items().length, 40);
  });
  
  
  QUnit.test("second setViewportItemIndex to first page not load next page", function(assert) {
      this.dataController.setViewportItemIndex(1);
      //act
      this.dataController.setViewportItemIndex(19);
  
      //assert
      assert.strictEqual(this.dataController.pageIndex(), 1);
      assert.strictEqual(this.dataController.hasKnownLastPage(), false);
      assert.strictEqual(this.dataController.items().length, 40);
  });
  
  QUnit.test("setViewportItemIndex to current page not load next page when hasKnownLastPage", function(assert) {
      //act
      this.dataController.setViewportItemIndex(1);
      this.dataController.setViewportItemIndex(21);
      this.dataController.setViewportItemIndex(41);
  
      //assert
      assert.strictEqual(this.dataController.pageIndex(), 2);
      assert.strictEqual(this.dataController.hasKnownLastPage(), true);
      assert.strictEqual(this.dataController.items().length, 50);
  });
  
  QUnit.test("reset pageIndex on refresh when appendMode", function(assert) {
      this.dataSource.load();
      this.dataController.pageIndex(1);
  
      //act
      this.dataController.refresh();
  
      assert.strictEqual(this.dataController.pageIndex(), 0);
  });
  
  //T180315
  QUnit.test("setViewportItemIndex before data loaded", function(assert) {
      this.applyOptions({
          loadingTimeout: 0
      });
  
      this.dataController.reload(true);
  
      //act
      this.dataController.setViewportItemIndex(1);
      this.clock.tick();
  
      //assert
      assert.strictEqual(this.dataController.pageIndex(), 0);
      assert.strictEqual(this.dataController.hasKnownLastPage(), false);
      assert.strictEqual(this.dataController.items().length, 20);
  });
  
  //T103389
  QUnit.test("load several pages when pageSize less then viewportSize", function(assert) {
      var changedArgs = [];
  
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      //act
      this.dataController.pageSize(4);
  
      //assert
      assert.deepEqual(this.dataController.items().length, 12);
      assert.equal(changedArgs.length, 3);
      assert.deepEqual(changedArgs[0].changeType, 'refresh');
      assert.deepEqual(changedArgs[0].items[0].key, 0);
      assert.deepEqual(changedArgs[1].changeType, 'append');
      assert.deepEqual(changedArgs[1].items[0].key, 4);
      assert.deepEqual(changedArgs[2].changeType, 'append');
      assert.deepEqual(changedArgs[2].items[0].key, 8);
      assert.ok(this.dataController.isLoaded());
  });
  
  //T103389
  QUnit.test("load several pages when pageSize less then viewportSize and preload enabled", function(assert) {
      var changedArgs = [];
  
      this.options.scrolling.preloadEnabled = true;
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      //act
      this.dataController.pageSize(4);
  
      //assert
      assert.deepEqual(this.dataController.items().length, 16);
      assert.equal(changedArgs.length, 4);
      assert.deepEqual(changedArgs[0].changeType, 'refresh');
      assert.deepEqual(changedArgs[0].items[0].key, 0);
      assert.deepEqual(changedArgs[1].changeType, 'append');
      assert.deepEqual(changedArgs[1].items[0].key, 4);
      assert.deepEqual(changedArgs[2].changeType, 'append');
      assert.deepEqual(changedArgs[2].items[0].key, 8);
      assert.deepEqual(changedArgs[3].changeType, 'append');
      assert.deepEqual(changedArgs[3].items[0].key, 12);
      assert.ok(this.dataController.isLoaded());
  });
  
  //T377458
  QUnit.test("last page does not be loaded several times then totalCount % pageSize === 0", function(assert) {
      var changedArgs = [];
  
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      this.dataController.viewportSize(30);
      this.dataController.pageSize(25);
  
      //act
      this.dataController.setViewportItemIndex(40);
  
      //assert
      assert.deepEqual(this.dataController.items().length, 50);
      assert.equal(changedArgs.length, 3);
      assert.deepEqual(changedArgs[0].changeType, 'refresh');
      assert.deepEqual(changedArgs[0].items[0].key, 0);
      assert.deepEqual(changedArgs[1].changeType, 'append');
      assert.deepEqual(changedArgs[1].items[0].key, 25);
      assert.deepEqual(changedArgs[2].changeType, 'append');
      assert.deepEqual(changedArgs[2].items, []);
      assert.ok(this.dataController.isLoaded());
  });
  
  //T507750
  QUnit.test("last page does not be loaded several times then totalCount equals pageSize", function(assert) {
      var changedArgs = [];
  
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      this.dataController.viewportSize(20);
      this.dataController.pageSize(50);
  
      //act
      this.dataController.setViewportItemIndex(30);
  
      //assert
      assert.deepEqual(this.dataController.items().length, 50);
      assert.equal(changedArgs.length, 2);
      assert.deepEqual(changedArgs[0].changeType, 'refresh');
      assert.deepEqual(changedArgs[0].items[0].key, 0);
      assert.deepEqual(changedArgs[1].changeType, 'append');
      assert.deepEqual(changedArgs[1].items, []);
      assert.ok(this.dataController.isLoaded());
  });
  
  QUnit.module("Infinite scrolling (ScrollingDataSource)", {
      beforeEach: function() {
  
          this.options = {
              scrolling: { mode: 'infinite' },
              grouping: { autoExpandAll: true }
          };
          setupModule.apply(this);
  
          this.setupDataSource = function(options) {
              this.dataSource = createDataSource(options.data || TEN_NUMBERS, {}, $.extend({ paginate: true, requireTotalCount: false }, options));
              this.dataController.setDataSource(this.dataSource);
              this.dataSource.load();
          };
  
          this.getDataItems = function(items) {
              var dataItems = [];
  
              items = items || this.dataController.items();
  
              $.each(items, function() {
                  dataItems.push(this.data);
              });
              return dataItems;
          };
  
          this.clock = sinon.useFakeTimers();
      }, afterEach: function() {
          teardownModule.call(this);
          this.clock.restore();
      }
  });
  
  QUnit.test("not update pageSize on viewportSize", function(assert) {
      var changedCallsCount = 0;
  
      this.setupDataSource({
          data: TEN_NUMBERS.concat(TEN_NUMBERS)
      });
  
      var dataController = this.dataController;
  
      dataController.changed.add(function(e) {
          changedCallsCount++;
      });
  
      //act
      dataController.viewportSize(2);
  
      //assert
      assert.equal(changedCallsCount, 0);
      assert.equal(this.getDataItems().length, 20);
      assert.equal(dataController.pageSize(), 20);
      assert.ok(dataController.isLoaded());
  });
  
  QUnit.test("change pageIndex", function(assert) {
      var changedCallsCount = 0,
          changedArgs;
  
      this.setupDataSource({
          pageSize: 3
      });
  
      var dataController = this.dataController;
  
      //act
      dataController.changed.add(function(e) {
          changedCallsCount++;
          changedArgs = e;
      });
  
      dataController.pageIndex(1);
  
      //assert
      assert.equal(changedCallsCount, 1);
      assert.deepEqual(changedArgs.changeType, 'append');
      assert.deepEqual(this.getDataItems(changedArgs.items), [4, 5, 6]);
      assert.deepEqual(this.getDataItems(), [1, 2, 3, 4, 5, 6]);
      assert.equal(dataController.pageIndex(), 1);
      assert.equal(dataController.pageSize(), 3);
      assert.ok(dataController.isLoaded());
  });
  
  //B254676
  QUnit.test("Load next page and return to previous", function(assert) {
      //arrange
      this.setupDataSource({
          asyncLoadEnabled: true,
          pageSize: 3
      });
  
      this.clock.tick();
  
      var dataController = this.dataController;
  
      //act
      dataController.pageIndex(1);
      dataController.load();
  
      dataController.pageIndex(0);
      dataController.load();
  
      this.clock.tick();
  
      //assert
      assert.equal(dataController.pageIndex(), 0, 'page index');
      assert.deepEqual(this.getDataItems(), [1, 2, 3, 4, 5, 6], 'items');
  });
  
  //B252339
  QUnit.test("reset pageIndex on reload", function(assert) {
      var changedCallsCount = 0,
          changedArgs;
  
      this.setupDataSource({
          pageSize: 3
      });
  
      var dataController = this.dataController;
  
  
      dataController.pageIndex(1);
      dataController.changed.add(function(e) {
          changedCallsCount++;
          changedArgs = e;
      });
  
      //act
      dataController.reload(true);
  
      //assert
      assert.equal(changedCallsCount, 1);
      assert.strictEqual(changedArgs.changeType, 'refresh');
      assert.deepEqual(this.getDataItems(changedArgs.items), [1, 2, 3]);
      assert.deepEqual(this.getDataItems(), [1, 2, 3]);
      assert.equal(dataController.pageIndex(), 0, 'pageIndex reset to 0');
      assert.equal(dataController.pageSize(), 3);
      assert.ok(dataController.isLoaded());
  });
  
  QUnit.module("Filtering", {
      beforeEach: function() {
          setupModule.apply(this);
  
          this.applyOptions({
              searchPanel: {
                  text: ""
              }
          });
  
          var originalOption = this.option;
  
          this.option = function(options, value) {
              var result = originalOption.apply(this, arguments);
  
              if(options === "searchPanel.text" && commonUtils.isDefined(value)) {
                  this.dataController.optionChanged({ fullName: options });
              }
  
              return result;
          };
  
          this.setupFilterableData = function() {
              this.dataSource = createDataSource([
                  { name: 'Alex', age: 15, birthDate: new Date(1999, 2, 5), state: 0, processed: false, category: 0, categoryName: "First Category" },
                  { name: 'Boris', age: 21, birthDate: new Date(1993, 5, 2), state: 0, processed: false, category: 1, categoryName: "Second Category" },
                  { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20), state: 1, processed: true, category: 2, categoryName: "Third Category" }
              ]);
              this.applyOptions({
                  columns: ['name', 'age', 'birthDate',
                      {
                          calculateCellValue: function(data) {
                              return data.name + ' ' + data.age;
                          },
                          calculateFilterExpression: function(text, operation) {
                              return [['name', operation || 'contains', text], 'or', ['age', operation || 'contains', text]];
                          }
                      },
                      {
                          dataField: 'state', lookup: {
                              dataSource: [
                                  { id: 0, state: 'Accept' },
                                  { id: 1, state: 'Reject' }
                              ],
                              displayExpr: 'state',
                              valueExpr: 'id'
                          }
                      }, { dataField: 'processed' }, {
                          dataField: 'category', calculateDisplayValue: "categoryName", lookup: {
                              dataSource: [
                                  { id: 0, name: "First Category" },
                                  { id: 1, name: "Second Category" },
                                  { id: 2, name: "Third Category" }
                              ],
                              displayExpr: 'name',
                              valueExpr: 'id'
                          }
                      }]
              });
              this.dataController.setDataSource(this.dataSource);
              this.dataSource.load();
          };
      },
      afterEach: teardownModule
  });
  
  QUnit.test("dataSource filter", function(assert) {
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 }
      ], {}, { filter: ['name', 'Dan'] });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  //B233497
  QUnit.test("dataSource filter option on date field defined as string", function(assert) {
      this.applyOptions({
          columns: ['name', { dataField: 'birthDate', dataType: 'date' }],
          dataSource: {
              asyncLoadEnabled: false,
              store: [
                  { name: 'Alex', birthDate: '1987/5/5' },
                  { name: 'Dan', birthDate: '1985/3/21' }
              ],
              filter: ['birthDate', new Date(1985, 2, 21)]
          }
      });
  
      //act
      this.dataController.optionChanged({ name: "dataSource" });
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
      //T320550
      assert.deepEqual(this.filter(), ['birthDate', new Date(1985, 2, 21)]);
  });
  
  //B233497
  QUnit.test("dataSource filter operation on date field defined as string", function(assert) {
      this.dataSource = createDataSource([
          { name: 'Alex', birthDate: '1987/5/5' },
          { name: 'Dan', birthDate: '1985/3/21' }
      ]);
  
      this.applyOptions({
          columns: ['name', { dataField: 'birthDate', dataType: 'date' }]
      });
  
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      //act
      this.dataController.filter(['birthDate', new Date(1985, 2, 21)]);
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  //T253839
  QUnit.test("dataSource filter option on date field defined as string and CustomStore is used with remoteOperations false", function(assert) {
      this.applyOptions({
          columns: ['name', { dataField: 'birthDate', dataType: 'date' }],
          remoteOperations: false,
          dataSource: {
              asyncLoadEnabled: false,
              load: function() {
                  return [
                      { name: 'Alex', birthDate: '1987/5/5' },
                      { name: 'Dan', birthDate: '1985/3/21' }
                  ];
              },
              filter: ['birthDate', new Date(1985, 2, 21)]
          }
      });
  
      //act
      this.dataController.optionChanged({ name: "dataSource" });
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  QUnit.test("grouping on date field defined as string", function(assert) {
      this.applyOptions({
          columns: ['name', { dataField: 'birthDate', dataType: 'date', groupIndex: 0 }],
          dataSource: {
              asyncLoadEnabled: false,
              store: [
                  { name: 'Alex', birthDate: '1987/5/5' },
                  { name: 'Dan', birthDate: '1985/3/21' }
              ]
          }
      });
  
      //act
      this.dataController.optionChanged({ name: "dataSource" });
  
      //assert
      assert.equal(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].data.key, new Date('1985/3/21'));
      assert.deepEqual(this.dataController.items()[1].data.key, new Date('1987/5/5'));
  });
  
  QUnit.test("grouping on date field defined as string assigned in customizeColumns", function(assert) {
      this.applyOptions({
          customizeColumns: function(columns) {
              columns[1].groupIndex = 0;
              columns[1].dataType = "date";
  
          },
          dataSource: {
              asyncLoadEnabled: false,
              store: [
                  { name: 'Alex', birthDate: '1987/5/5' },
                  { name: 'Dan', birthDate: '1985/3/21' }
              ]
          }
      });
  
      //act
      this.dataController.optionChanged({ name: "dataSource" });
  
      //assert
      assert.equal(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].data.key, new Date('1985/3/21'));
      assert.deepEqual(this.dataController.items()[1].data.key, new Date('1987/5/5'));
  });
  
  QUnit.test("change filter as array argument", function(assert) {
      //arrange
      var countCallPageChanged = 0;
  
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 }
      ], {}, { filter: ['name', 'Dan'] });
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      this.dataController.pageChanged.add(function() {
          countCallPageChanged++;
      });
  
      //act
      this.dataController.filter(['name', 'startswith', 'A']);
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  
      //T262949
      assert.equal(countCallPageChanged, 1, "count call pageChanged");
  });
  
  //T413302
  QUnit.test("filter as null - no apply when there is no dataSource filter", function(assert) {
      //arrange
      var countCallPageChanged = 0;
  
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 }
      ]);
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      this.dataController.pageChanged.add(function() {
          countCallPageChanged++;
      });
  
      //act
      this.dataController.filter(null);
  
      //assert
      assert.equal(this.dataController.items().length, 2, "count item");
      assert.equal(countCallPageChanged, 0, "not call pageChanged");
  });
  
  //T413302
  QUnit.test("filter as arguments - no apply when the filter isn't changed", function(assert) {
      //arrange
      var countCallPageChanged = 0;
  
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 }
      ], {}, { filter: ['name', 'Dan'] });
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      this.dataController.pageChanged.add(function() {
          countCallPageChanged++;
      });
  
      //act
      this.dataController.filter(['name', 'Dan']);
  
      //assert
      assert.equal(this.dataController.items().length, 1, "count item");
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
      assert.equal(countCallPageChanged, 0, "not call pageChanged");
  });
  
  QUnit.test("change filter as arguments", function(assert) {
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 }
      ], {}, { filter: ['name', 'Dan'] });
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //act
      this.dataController.filter('name', 'startswith', 'A');
  
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("change filter as function", function(assert) {
      var loadingCount = 0;
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 }
      ], { onLoading: function() { loadingCount++; } }, { filter: ['name', 'Dan'] });
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //act
      this.dataController.filter(function(data) {
          return data.age > 15;
      });
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  
      //act
      this.dataController.filter(function(data) {
          return data.age === 15;
      });
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
      assert.equal(loadingCount, 1, 'one loading');
  });
  
  QUnit.test("get filter and combinedFilter", function(assert) {
      this.dataSource = new DataSource({
          load: function() {
              return [];
          },
          filter: ['name', '=', 'Alex']
      });
  
      this.applyOptions({
          remoteOperations: { filtering: true, sorting: true, paging: true },
          columns: [{ dataField: 'name', dataType: 'string', filterValue: 'A' }, { dataField: 'age', dataType: 'number', filterValue: 15 }],
          searchPanel: {
              text: 'Al'
          }
      });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //act
      assert.deepEqual(this.filter(), ['name', '=', 'Alex']);
      assert.deepEqual(this.getCombinedFilter(), [
          [
              [["name", "contains", "A"], "and", ["age", "=", 15]],
              "and",
              ["name", "contains", "Al"]
          ],
          "and",
          ['name', '=', 'Alex']
      ]);
  });
  
  QUnit.test("get combinedFilter for search when allowSearch false", function(assert) {
      this.dataSource = new DataSource({
          load: function() {
              return [];
          },
          filter: ['name', '=', 'Alex']
      });
  
      this.applyOptions({
          remoteOperations: { filtering: true, sorting: true, paging: true },
          columns: [{ dataField: 'name', dataType: 'string', allowSearch: false }, { dataField: 'age', dataType: 'string', visible: false }],
          searchPanel: {
              text: 'Al'
          }
      });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //act
      assert.deepEqual(this.filter(), ['name', '=', 'Alex']);
      assert.deepEqual(this.getCombinedFilter(), [
          ["age", "contains", "Al"],
          "and",
          ['name', '=', 'Alex']
      ]);
  });
  
  QUnit.test("get combinedFilter for search when searchVisibleColumnsOnly true and column is not visible", function(assert) {
      this.dataSource = new DataSource({
          load: function() {
              return [];
          },
          filter: ['name', '=', 'Alex']
      });
  
      this.applyOptions({
          remoteOperations: { filtering: true, sorting: true, paging: true },
          columns: [{ dataField: 'name', dataType: 'string' }, { dataField: 'age', dataType: 'string' }],
          searchPanel: {
              text: 'Al',
              searchVisibleColumnsOnly: true
          }
      });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //assert
      assert.deepEqual(this.filter(), ['name', '=', 'Alex']);
      assert.deepEqual(this.getCombinedFilter(), [
          [["name", "contains", "Al"], "or", ["age", "contains", "Al"]],
          "and",
          ['name', '=', 'Alex']
      ]);
  
      //act
      this.columnOption("name", "visible", false);
  
      //assert
      assert.deepEqual(this.getCombinedFilter(), [
          ["age", "contains", "Al"],
          "and",
          ['name', '=', 'Alex']
      ]);
  });
  
  QUnit.test("get combinedFilter for search when allowFiltering false", function(assert) {
      this.dataSource = new DataSource({
          load: function() {
              return [];
          },
          filter: ['name', '=', 'Alex']
      });
  
      this.applyOptions({
          remoteOperations: { filtering: true, sorting: true, paging: true },
          columns: [{ dataField: 'name', dataType: 'string', allowFiltering: false }, { dataField: 'age', dataType: 'string' }],
          searchPanel: {
              text: 'Al'
          }
      });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //act
      assert.deepEqual(this.filter(), ['name', '=', 'Alex']);
      assert.deepEqual(this.getCombinedFilter(), [
          ["age", "contains", "Al"],
          "and",
          ['name', '=', 'Alex']
      ]);
  });
  
  QUnit.test("get combinedFilter for search when allowFiltering false and allowSearch true", function(assert) {
      this.dataSource = new DataSource({
          load: function() {
              return [];
          },
          filter: ['name', '=', 'Alex']
      });
  
      this.applyOptions({
          remoteOperations: { filtering: true, sorting: true, paging: true },
          columns: [{ dataField: 'name', dataType: 'string', allowFiltering: false, allowSearch: true }, { dataField: 'age', dataType: 'string' }],
          searchPanel: {
              text: 'Al'
          }
      });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //act
      assert.deepEqual(this.filter(), ['name', '=', 'Alex']);
      assert.deepEqual(this.getCombinedFilter(), [
          [
              ["name", "contains", "Al"],
              "or",
              ["age", "contains", "Al"]
          ],
          "and",
          ['name', '=', 'Alex']
      ]);
  });
  
  //T373345
  QUnit.test("get combinedFilter when filterRow between operation and headerFilter is used", function(assert) {
      this.dataSource = new DataSource({
          load: function() {
              return [];
          }
      });
  
      this.applyOptions({
          remoteOperations: true,
          columns: [{ dataField: 'age', dataType: 'number', filterValue: [15, 20], selectedFilterOperation: "between", filterValues: [17] }]
      });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      //act
      assert.deepEqual(this.getCombinedFilter(), [
          [["age", ">=", 15], "and", ["age", "<=", 20]],
          "and",
          ["age", "=", 17]
      ]);
  });
  
  //T203533
  QUnit.test("get combinedFilter with filter on only one column", function(assert) {
      //arrange
      this.dataSource = new DataSource({
          load: function() {
              return [];
          },
          filter: ['name', '=', 'Alex']
      });
  
      this.applyOptions({
          remoteOperations: { filtering: true, sorting: true, paging: true },
          columns: [{ dataField: 'name', dataType: 'string' }, { dataField: 'age', dataType: 'number', filterValue: 15 }]
      });
  
      //act
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      //assert
      assert.deepEqual(this.filter(), ['name', '=', 'Alex']);
      assert.deepEqual(this.getCombinedFilter(), [
          ["age", "=", 15],
          "and",
          ['name', '=', 'Alex']
      ]);
  });
  
  QUnit.test("get combinedFilter with argument", function(assert) {
      //arrange
      var filter;
  
      this.dataSource = new DataSource({
          load: function() {
              return [];
          },
          filter: ["age", "=", 15]
      });
  
      this.applyOptions({
          remoteOperations: false,
          columns: [{ dataField: "name", dataType: "string", filterValue: "Alex" }, { dataField: "age", dataType: "number" }]
      });
  
      //act
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      //assert
      filter = this.getCombinedFilter();
      assert.ok($.isArray(filter[0]), "first argument is filter expression");
      assert.strictEqual(filter[1], "and", "second argument is operation 'and'");
      assert.ok($.isArray(filter[2]), "third argument is filter expression");
      assert.ok($.isFunction(filter[0][0]), "filter expression selector is function");
  
      filter = this.getCombinedFilter(true);
      assert.ok($.isArray(filter[0]), "first argument is filter expression");
      assert.strictEqual(filter[1], "and", "second argument is operation 'and'");
      assert.ok($.isArray(filter[2]), "third argument is filter expression");
      assert.equal(filter.length, 3, "filter expression + 'and' + 'filter expression'");
      assert.ok(!$.isFunction(filter[2][0]), "filter expression selector isn't function");
      assert.strictEqual(filter[0][0], "name", "value of the selector");
  });
  
  QUnit.test("get combinedFilter with argument for date value with ISO8601 parsing", function(assert) {
      var defaultForceIsoDateParsing = config().forceIsoDateParsing;
      config().forceIsoDateParsing = true;
  
      try {
          //arrange
          var filter;
  
          this.dataSource = new DataSource({
              load: function() {
                  return [{ date: "2016-03-02T10:59:00" }];
              },
              filter: ["date", "<", new Date(2017, 0, 1)]
          });
  
          this.applyOptions({
              remoteOperations: false,
              columns: [{ dataField: "date", dataType: "date", selectedFilterOperation: ">=", filterValue: new Date(2016, 0, 1) }]
          });
  
          //act
          this.dataController.setDataSource(this.dataSource);
          this.dataSource.load();
  
          //assert
          filter = this.getCombinedFilter(true);
          assert.deepEqual(filter, [["date", ">=", "2016-01-01T00:00:00"], "and", ["date", "<", "2017-01-01T00:00:00"]], "filter with serialized dates");
      } finally {
          config().forceIsoDateParsing = defaultForceIsoDateParsing;
      }
  });
  
  QUnit.test("get combinedFilter with argument for date value without ISO8601 parsing", function(assert) {
      var defaultForceIsoDateParsing = config().forceIsoDateParsing;
      config().forceIsoDateParsing = false;
  
      try {
          //arrange
          var filter;
  
          this.dataSource = new DataSource({
              load: function() {
                  return [{ date: "2016-03-02T10:59:00" }];
              },
              filter: ["date", "<", new Date(2017, 0, 1)]
          });
  
          this.applyOptions({
              remoteOperations: false,
              columns: [{ dataField: "date", dataType: "date", selectedFilterOperation: ">=", filterValue: new Date(2016, 0, 1) }]
          });
  
          //act
          this.dataController.setDataSource(this.dataSource);
          this.dataSource.load();
  
          //assert
          filter = this.getCombinedFilter(true);
          assert.deepEqual(filter, [["date", ">=", new Date(2016, 0, 1)], "and", ["date", "<", new Date(2017, 0, 1)]], "filter with serialized dates");
      } finally {
          config().forceIsoDateParsing = defaultForceIsoDateParsing;
      }
  });
  
  //T353244
  QUnit.test("changing filterType without filterValues do not rise changed event", function(assert) {
      //arrange
      var that = this,
          countCallChanged = 0;
  
      that.options.loadingTimeout = 0;
      that.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Tom', age: 12 },
          { name: 'Bob', age: 19 },
          { name: 'Bobbi', age: 19 }
      ]);
  
      that.applyOptions({
          columns: [{ dataField: "name" }, { dataField: "age", filterValue: 19 }]
      });
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      that.clock.tick();
  
      //arrange
      that.dataController.changed.add(function() {
          countCallChanged++;
      });
  
      //act
      that.columnOption("name", "filterType", "exclude");
      that.clock.tick();
  
      //assert
      assert.strictEqual(that.columnsController.getColumns()[0].filterType, "exclude", "filterType is changed");
      assert.strictEqual(countCallChanged, 0, "changed is not raised");
  });
  
  //T238430
  QUnit.test("clearFilter without argument", function(assert) {
      //arrange
      var that = this,
          items,
          columns,
          countCallChanged = 0;
  
      that.options.loadingTimeout = 0;
      that.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Tom', age: 12 },
          { name: 'Bob', age: 19 },
          { name: 'Bobbi', age: 19 }
      ], {}, { filter: [['name', 'Alex'], "or", ['name', 'Dan'], "or", ['name', 'Tom'], "or", ['name', 'Bob']] });
  
      that.applyOptions({
          columns: [{ dataField: "name", filterValues: ["Alex", "Dan", "Bob"] }, { dataField: "age", filterValue: 19 }]
      });
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      that.dataController.searchByText("Bob");
      that.clock.tick();
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  
      //arrange
      that.dataController.changed.add(function() {
          countCallChanged++;
      });
  
      //act
      that.dataController.clearFilter();
      that.clock.tick();
  
      //assert
      columns = that.columnsController.getColumns();
      assert.equal(countCallChanged, 1, "count call changed");
      assert.equal(that.dataController.items().length, 5, "count items");
      assert.strictEqual(that.option("searchPanel.text"), "", "search text");
      assert.deepEqual(that.dataController.filter(), null, "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, undefined, "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, undefined, "filter value of the second column");
  });
  
  //T238430
  QUnit.test("clearFilter for dataSource", function(assert) {
      //arrange
      var that = this,
          items,
          columns;
  
      that.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Tom', age: 12 },
          { name: 'Bob', age: 19 },
          { name: 'Bobbi', age: 19 }
      ], {}, { filter: [['name', 'Alex'], "or", ['name', 'Dan'], "or", ['name', 'Tom'], "or", ['name', 'Bob']] });
  
      that.applyOptions({
          columns: [{ dataField: "name", filterValues: ["Alex", "Dan", "Bob", "Bobbi"] }, { dataField: "age", filterValue: 19 }]
      });
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      that.dataController.searchByText("Bob");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob", "Bobbi"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  
      //act
      that.dataController.clearFilter("dataSource");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 2, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data of the first item");
      assert.deepEqual(items[1].data, { name: 'Bobbi', age: 19 }, "data of the second item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.deepEqual(that.dataController.filter(), null, "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob", "Bobbi"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  });
  
  //T238430
  QUnit.test("clearFilter for search", function(assert) {
      //arrange
      var that = this,
          items,
          columns;
  
      that.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Tom', age: 12 },
          { name: 'Bob', age: 19 },
          { name: 'Bobbi', age: 19 }
      ], {}, { filter: [['name', 'Alex'], "or", ['name', 'Dan'], "or", ['name', 'Tom'], "or", ['name', 'Bob']] });
  
      that.applyOptions({
          columns: [{ dataField: "name", filterValues: ["Alex", "Dan", "Bob"] }, { dataField: "age", filterValue: 19 }]
      });
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      that.dataController.searchByText("Bob");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  
      //act
      that.dataController.clearFilter("search");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 2, "count items");
      assert.deepEqual(items[0].data, { name: 'Dan', age: 19 }, "data of the first item");
      assert.deepEqual(items[1].data, { name: 'Bob', age: 19 }, "data of the second item");
      assert.strictEqual(that.option("searchPanel.text"), "", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  });
  
  //T238430
  QUnit.test("clearFilter for filter row", function(assert) {
      //arrange
      var that = this,
          items,
          columns;
  
      that.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Tom', age: 12 },
          { name: 'Bob', age: 19 },
          { name: 'Bobbi', age: 19 }
      ], {}, { filter: [['name', 'Alex'], "or", ['name', 'Dan'], "or", ['name', 'Tom'], "or", ['name', 'Bob']] });
  
      that.applyOptions({
          columns: [{ dataField: "name", filterValues: ["Alex", "Dan", "Bob"] }, { dataField: "age", filterValue: 19 }]
      });
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      that.dataController.searchByText("Bob");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  
      //act
      that.dataController.clearFilter("row");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data of the second item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, undefined, "filter value of the second column");
  });
  
  //T238430
  QUnit.test("clearFilter for header filter", function(assert) {
      //arrange
      var that = this,
          items,
          columns;
  
      that.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Tom', age: 12 },
          { name: 'Bob', age: 19 },
          { name: 'Bobbi', age: 19 }
      ], {}, { filter: [['name', 'Alex'], "or", ['name', 'Dan'], "or", ['name', 'Tom'], "or", ['name', 'Bob']] });
  
      that.applyOptions({
          columns: [{ dataField: "name", filterValues: ["Alex", "Dan", "Bob"] }, { dataField: "age", filterValue: 19 }]
      });
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      that.dataController.searchByText("Bob");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  
      //act
      that.dataController.clearFilter("header");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data of the second item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, undefined, "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  });
  
  //T238430
  QUnit.test("clearFilter didn't apply by incorrect filter name", function(assert) {
      //arrange
      var that = this,
          items,
          columns;
  
      that.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Tom', age: 12 },
          { name: 'Bob', age: 19 },
          { name: 'Bobbi', age: 19 }
      ], {}, { filter: [['name', 'Alex'], "or", ['name', 'Dan'], "or", ['name', 'Tom'], "or", ['name', 'Bob']] });
  
      that.applyOptions({
          columns: [{ dataField: "name", filterValues: ["Alex", "Dan", "Bob"] }, { dataField: "age", filterValue: 19 }]
      });
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      that.dataController.searchByText("Bob");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  
      //act
      that.dataController.clearFilter("test");
  
      //assert
      items = that.dataController.items();
      columns = that.columnsController.getColumns();
      assert.equal(items.length, 1, "count items");
      assert.deepEqual(items[0].data, { name: 'Bob', age: 19 }, "data of the second item");
      assert.strictEqual(that.option("searchPanel.text"), "Bob", "search text");
      assert.ok(commonUtils.isArray(that.dataController.filter()), "filter dataSource");
      assert.equal(columns.length, 2, "count columns");
      assert.deepEqual(columns[0].filterValues, ["Alex", "Dan", "Bob"], "filter values of the first column");
      assert.deepEqual(columns[1].filterValue, 19, "filter values", "filter value of the second column");
  });
  
  //B233043
  QUnit.test("filter operation reset pageIndex", function(assert) {
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15 },
          { name: 'Dan', age: 19 },
          { name: 'Max', age: 21 }
      ], {}, { pageSize: 2, pageIndex: 1 });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
  
      assert.equal(this.dataController.pageIndex(), 1);
  
      //act
      this.dataController.filter(['name', '<>', 'Ilya']);
  
      assert.equal(this.dataController.items().length, 2);
      assert.equal(this.dataController.pageIndex(), 0);
  });
  
  QUnit.test("search for number column", function(assert) {
      this.setupFilterableData();
  
      //act
      this.dataController.searchByText('9');
  
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  QUnit.test("search for string column", function(assert) {
      this.setupFilterableData();
  
      //act
      this.dataController.searchByText('Al');
  
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("search for date column", function(assert) {
      this.setupFilterableData();
  
      //act
      this.dataController.searchByText(formatHelper.format(new Date(1996, 1, 20), 'shortDate'));
  
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  QUnit.test("search and dataSource filter", function(assert) {
      this.dataSource = createDataSource([
          { name: 'Alex', age: 15, birthDate: new Date(1999, 2, 5) },
          { name: 'Boris', age: 21, birthDate: new Date(1993, 5, 2) },
          { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20) }
      ], {}, { filter: ['age', '>', 16] });
  
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
      assert.equal(this.dataController.items().length, 2);
  
      //act
      this.dataController.searchByText('B');
  
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Boris');
  });
  
  //T100310
  QUnit.test("search for lookup column", function(assert) {
      this.setupFilterableData();
  
      //act
      this.dataController.searchByText('Reje');
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  QUnit.test("search for lookup column with calculateDisplayValue", function(assert) {
      this.setupFilterableData();
  
      //act
      this.dataController.searchByText('Secon');
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Boris');
  });
  
  //B239940
  QUnit.test("Apply search for string column when change dataSource", function(assert) {
      //arrange
      var that = this,
          logId,
          isDataSourceReloaded = false;
  
      that.setupFilterableData();
  
      //act
      that.dataController.searchByText('Test');
  
      that.dataSource = createDataSource([
          { name: 'Test', age: 15, birthDate: new Date(1999, 2, 5) },
          { name: 'Boris', age: 21, birthDate: new Date(1993, 5, 2) },
          { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20) }
      ]);
  
      that.dataSource.on("changed", function() {
          isDataSourceReloaded = true;
      });
  
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
      //assert
      assert.ok(isDataSourceReloaded);
      assert.equal(logId, undefined, 'no log messages');
      assert.equal(that.dataController.items().length, 1);
      assert.equal(that.dataController.items()[0].data.name, 'Test');
  });
  
  QUnit.test("Apply search for string and number column when change dataSource", function(assert) {
      //arrange
      var that = this;
  
      //act
      that.dataController.searchByText('20');
  
      that.dataSource = new DataSource({
          _preferSync: true,
          store: [
              { stringField: 'Test150', numberField: 20 },
              { stringField: 'Test200', numberField: 200 },
              { stringField: 'Test350', numberField: 2000 }
          ]
      });
  
      that.dataController.setDataSource(that.dataSource);
  
      that.dataSource.load();
      //assert
      assert.equal(that.dataController.items().length, 2);
      assert.equal(that.dataController.items()[0].data.numberField, 20, '20 equals searchText "20"');
      assert.equal(that.dataController.items()[1].data.stringField, 'Test200', '200 contains searchText "20"');
  });
  
  QUnit.test("Apply search for string column without reloading when all dataTypes defined", function(assert) {
      //arrange
      var that = this,
          logId,
          loadCount = 0;
  
      this.applyOptions({
          searchPanel: { text: 'Test' },
          columns: [{ dataField: 'name', dataType: 'string' }, { dataField: 'birthDate', dataType: 'date' }, { dataField: 'age', dataType: 'number' }]
      });
  
  
      //act
      that.dataSource = createDataSource([
          { name: 'Test', age: 15, birthDate: new Date(1999, 2, 5) },
          { name: 'Boris', age: 21, birthDate: new Date(1993, 5, 2) },
          { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20) }
      ]);
  
      that.dataSource.on('changed', function() {
          loadCount++;
      });
  
      that.dataController.setDataSource(that.dataSource);
  
      that.dataSource.load();
      //assert
      assert.equal(loadCount, 1);
      assert.equal(logId, undefined);
      assert.equal(that.dataController.items().length, 1);
      assert.equal(that.dataController.items()[0].data.name, 'Test');
  });
  
  QUnit.test("column filter for one column", function(assert) {
      this.setupFilterableData();
  
  
      //act
      this.columnsController.columnOption(0, 'filterValue', 'Al');
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("Filtering with additional filter when dataController's filter expression is function (assertT149995)", function(assert) {
      var dataSource = createDataSource([
                  { name: 'Alex', age: 15, birthDate: new Date(1999, 2, 5), state: 0, processed: false },
                  { name: 'Alla', age: 21, birthDate: new Date(1993, 5, 2), state: 0, processed: false },
                  { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20), state: 1, processed: true }
          ]),
          columnFilterItemsCount;
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      this.columnsController.columnOption('name', 'filterValue', 'Al');
  
      //act
      columnFilterItemsCount = this.dataController.items().length;
  
      this.dataController.filter(function(item) {
          return item.age < 20;
      });
  
      //assert
      assert.equal(columnFilterItemsCount, 2, "When change filterValue in filterRow we have 2 items in data controller");
      assert.equal(this.dataController.items().length, 1, "When we set data controller's filter we have 1 item in data controller which satisfies both filter expressions");
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("column filter for one column for custom defaultFilterOperation", function(assert) {
      this.setupFilterableData();
  
      //assert
      assert.equal(this.columnsController.columnOption(0, 'defaultFilterOperation'), 'contains');
  
      //act
      this.columnsController.columnOption(0, 'defaultFilterOperation', '=');
      this.columnsController.columnOption(0, 'filterValue', 'Al');
  
      //assert
      assert.equal(this.dataController.items().length, 0);
  
      //act
      this.columnsController.columnOption(0, 'filterValue', 'Alex');
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("column filter not apply for invisible column", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(0, 'visible', false);
      this.columnsController.columnOption(0, 'filterValue', 'Al');
  
      //assert
      assert.equal(this.dataController.items().length, 3);
  });
  
  QUnit.test("column filter for two columns", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(0, 'filterValue', 'A');
      this.columnsController.columnOption(1, 'filterValue', '19');
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  QUnit.test("column filter for column with lookup", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(4, 'filterValue', 1);
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  QUnit.test("column filter for column with lookup and calculateDisplayValue", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(6, 'filterValue', 1);
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Boris');
  });
  
  //T104792
  QUnit.test("column filter for boolean column true value", function(assert) {
      this.setupFilterableData();
  
  
      //act
      this.columnsController.columnOption(5, 'filterValue', true);
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
  });
  
  //T104792
  QUnit.test("column filter for boolean column false value", function(assert) {
      this.setupFilterableData();
  
  
      //act
      this.columnsController.columnOption(5, 'filterValue', false);
  
      //assert
      assert.equal(this.dataController.items().length, 2);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
      assert.equal(this.dataController.items()[1].data.name, 'Boris');
  });
  
  QUnit.test("column filter for column with calculateFilterExpression", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(3, 'filterValue', '9');
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.age, 19);
      //act
      this.columnsController.columnOption(3, 'filterValue', 'Ale');
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("column filter for column with calculateFilterExpression using function selector when cache enabled", function(assert) {
      this.setupFilterableData();
  
      var loadingCount = 0;
      this.dataSource.store().on("loading", function() {
          loadingCount++;
      });
  
      //act
      this.columnsController.columnOption(3, 'calculateFilterExpression', function(text, operation) {
          return [function(data) { return data.name + " " + data.age; }, operation || 'contains', text];
      });
      this.columnsController.columnOption(3, 'filterValue', '9');
  
      //assert
      assert.equal(loadingCount, 0, "no loading because cache enabled");
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.age, 19);
  
      //act
      this.columnsController.columnOption(3, 'filterValue', 'Ale');
  
      //assert
      assert.equal(loadingCount, 0, "no loading because cache enabled");
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("column filter for column with calculateFilterExpression returns filter function when cache enabled", function(assert) {
      this.setupFilterableData();
  
      var loadingCount = 0;
      this.dataSource.store().on("loading", function() {
          loadingCount++;
      });
  
      //act
      this.dataController.filter(["age", ">", 10]);
      this.columnsController.columnOption(3, 'calculateFilterExpression', function(text, operation) {
          return function(data) { return (data.name + " " + data.age).indexOf(text) >= 0; };
      });
      this.columnsController.columnOption(3, 'filterValue', '9');
  
      //assert
      assert.equal(loadingCount, 0, "no loading because cache enabled");
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.age, 19);
  
      //act
      this.columnsController.columnOption(3, 'filterValue', 'Ale');
  
      //assert
      assert.equal(loadingCount, 0, "no loading because cache enabled");
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Alex');
  });
  
  QUnit.test("no loading when cache enabled and remote filtering is not changed", function(assert) {
      this.setupFilterableData();
  
      this.applyOptions({
          remoteOperations: { filtering: true }
      });
  
      this.dataController._isSharedDataSource = true;
      this.dataController.setDataSource(this.dataSource);
  
      var loadingCount = 0;
      this.dataSource.store().on("loading", function() {
          loadingCount++;
      });
  
      //act
      this.columnsController.columnOption(3, 'calculateFilterExpression', function(text, operation) {
          return [function(data) { return data.name + " " + data.age; }, operation || 'contains', text];
      });
      this.columnsController.columnOption(3, 'filterValue', '9');
  
      //assert
      assert.equal(loadingCount, 1, "one loading after change filterValue");
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.age, 19);
  
      //act
      this.dataSource.load();
  
      //assert
      assert.equal(loadingCount, 1, "no loading because filter is not changed");
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.age, 19);
  });
  
  QUnit.test("column filter when selectedFilterOperation and filterValue parameters defined", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(0, {
          selectedFilterOperation: '>',
          filterValue: 'B'
      });
  
      //assert
      assert.equal(this.dataController.items().length, 2);
      assert.equal(this.dataController.items()[0].data.name, 'Boris');
      assert.equal(this.dataController.items()[1].data.name, 'Dan');
  });
  
  //T311798
  QUnit.test("column filter when selectedFilterOperation, filterValue and headerFilter.groupInterval parameters defined", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(1, {
          headerFilter: { groupInterval: 10 },
          selectedFilterOperation: '=',
          filterValue: 19
      });
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Dan');
      assert.equal(this.dataController.items()[0].data.age, 19);
  });
  
  
  QUnit.test("column filter when selectedFilterOperation and filterValue parameters undefined", function(assert) {
      //arrange
      this.setupFilterableData();
  
      this.applyOptions({
          columns: [{ dataField: 'name' }, 'age']
      });
  
      this.columnsController.columnOption(0, {
          selectedFilterOperation: "contains",
          filterValue: "Al"
      });
  
      //assert
      assert.strictEqual(this.columnsController.getVisibleColumns()[0].filterValue, "Al", "has filter text");
      assert.equal(this.dataController.items().length, 1, "count items");
      assert.strictEqual(this.dataController.items()[0].data.name, "Alex", "data name items[0]");
  
      //act
      this.columnsController.columnOption(0, {
          selectedFilterOperation: null,
          filterValue: null
      });
  
      //assert
      assert.ok(!this.columnsController.getVisibleColumns()[0].filterValue, "not has filter text");
      assert.ok(!this.columnsController.getVisibleColumns()[0].selectedFilterOperation, "not has selectedFilterOperation");
      assert.equal(this.dataController.items().length, 3, "count items");
  });
  
  QUnit.test("column filter when selectedFilterOperation defined and filterValue undefined", function(assert) {
      //arrange
      this.setupFilterableData();
      var dataSourceChanged = false;
  
      this.applyOptions({
          columns: [{ dataField: 'name', selectedFilterOperation: "contains", filterValue: "Al" }, 'age']
      });
  
      this.dataController._dataSource.changed.add(function() {
          dataSourceChanged = true;
      });
  
      //act
      this.columnsController.columnOption(0, {
          filterValue: null
      });
  
      //assert
      assert.ok(dataSourceChanged, "dataSource changed");
  });
  
  QUnit.test("column filter when selectedFilterOperation parameter null and column filterValue defined", function(assert) {
      //arrange
      var dataSourceChanged = false;
      this.setupFilterableData();
  
      this.applyOptions({
          columns: [{ dataField: 'name', selectedFilterOperation: "contains", filterValue: "Al" }, 'age']
      });
  
      this.dataController._dataSource.changed.add(function() {
          dataSourceChanged = true;
      });
  
      //act
      this.columnsController.columnOption(0, {
          selectedFilterOperation: null
      });
  
      //assert
      assert.ok(dataSourceChanged, "dataSource changed");
  });
  
  QUnit.test("column filter when selectedFilterOperation parameter defined and column filterValue is not defined", function(assert) {
      //arrange
      this.setupFilterableData();
      var dataSourceChanged = false;
  
      this.applyOptions({
          columns: [{ dataField: 'name', selectedFilterOperation: "contains" }, 'age']
      });
  
      this.dataController._dataSource.changed.add(function() {
          dataSourceChanged = true;
      });
  
      //act
      this.columnsController.columnOption(0, {
          selectedFilterOperation: '='
      });
  
      //assert
      assert.ok(!dataSourceChanged, "dataSource changed");
  });
  
  QUnit.test("column filter when selectedFilterOperation parameter defined and no filterValue", function(assert) {
      this.setupFilterableData();
  
      //act
      this.columnsController.columnOption(0, {
          selectedFilterOperation: '=',
          filterValue: undefined
      });
  
      //assert
      assert.equal(this.dataController.items().length, 3);
  });
  
  //B239940
  QUnit.test("Apply column filter when change dataSource", function(assert) {
      //arrange
      var that = this,
          isDataSourceReloaded = false;
  
      that.setupFilterableData();
  
      that.columnsController.columnOption(0, {
          selectedFilterOperation: '=',
          filterValue: 'Test'
      });
  
      that.dataSource = createDataSource([
          { name: 'Test', age: 15, birthDate: new Date(1999, 2, 5) },
          { name: 'Boris', age: 21, birthDate: new Date(1993, 5, 2) },
          { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20) }
      ]);
  
      that.dataSource.on("changed", function() {
          isDataSourceReloaded = true;
      });
  
      //act
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
  
      //assert
      assert.ok(isDataSourceReloaded);
      assert.equal(that.dataController.items().length, 1);
      assert.equal(that.dataController.items()[0].data.name, 'Test');
  });
  
  //B254794
  QUnit.test("Apply column filter on QUnit.start when data need converting", function(assert) {
      //arrange
      this.applyOptions({ columns: [{ dataField: 'name', selectedFilterOperation: '=', filterValue: 'Test' }, 'age', { dataField: 'birthDate', dataType: 'date' }] });
  
      this.dataSource = createDataSource([
          { name: 'Test', age: 15, birthDate: '1999/03/05' },
          { name: 'Boris', age: 21, birthDate: '1993/06/02' },
          { name: 'Dan', age: 19, birthDate: '1996/02/20' }
      ], {}, { asyncLoadEnabled: true });
  
      //act
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.items().length, 1);
      assert.equal(this.dataController.items()[0].data.name, 'Test');
      assert.deepEqual(this.dataController.items()[0].data.birthDate, '1999/03/05');
  });
  
  //B239940
  QUnit.test("Data source is not reloaded on set dataSource when no filters", function(assert) {
      //arrange
      var that = this,
          changedCallCount = 0;
  
      that.setupFilterableData();
  
      that.dataSource = createDataSource([
          { name: 'Test', age: 15, birthDate: new Date(1999, 2, 5) },
          { name: 'Boris', age: 21, birthDate: new Date(1993, 5, 2) },
          { name: 'Dan', age: 19, birthDate: new Date(1996, 1, 20) }
      ]);
  
      that.dataSource.on("changed", function() {
          changedCallCount++;
      });
  
      //act
      that.columnsController.reset();
      that.dataController.setDataSource(that.dataSource);
      that.dataSource.load();
  
      //assert
      assert.equal(changedCallCount, 1, 'one load only');
      assert.equal(that.dataController.items().length, 3);
      assert.equal(that.dataController.items()[0].data.name, 'Test');
  });
  
  QUnit.test("Apply filter method is not used when apply filter is 'OnClick' for filterRow", function(assert) {
      var isApplyFilterCalled;
  
      this.applyOptions({
          filterRow: {
              applyFilter: "onClick"
          }
      });
  
      this.dataController._applyFilter = function() {
          isApplyFilterCalled = true;
      };
  
      this.dataController._handleColumnsChanged({
          changeTypes: {
              columns: "test"
          },
          optionNames: {
              bufferedFilterValue: "test"
          }
      });
  
      assert.ok(!isApplyFilterCalled);
  });
  
  QUnit.test("Filter is reset when column has filterValue and it visibility is changed", function(assert) {
      this.setupFilterableData();
  
      this.columnsController.columnOption("name", "filterValue", "wrong");
      this.columnsController.columnOption("name", "visible", false);
  
      assert.equal(this.dataController.items().length, 3, "filter is reset");
  });
  
  QUnit.test("Filter is reset when column has filterValue and filterValues and it visibility is changed", function(assert) {
      this.setupFilterableData();
  
      this.columnsController.columnOption("name", {
          filterValue: "wrong1",
          filterValues: ["wrong2"]
      });
      this.columnsController.columnOption("name", "visible", false);
  
      assert.equal(this.dataController.items().length, 3, "filter is reset");
  });
  
  QUnit.test("Filter is reset when column has filterValues and it visibility is changed", function(assert) {
      this.setupFilterableData();
  
      this.columnsController.columnOption("name", "filterValues", ["wrong"]);
      this.columnsController.columnOption("name", "visible", false);
  
      assert.equal(this.dataController.items().length, 3, "filter is reset");
  });
  
  QUnit.test("Filter is applied when column with filterValue is shown", function(assert) {
      this.setupFilterableData();
  
      this.columnsController.columnOption("name", {
          filterValue: "wrong",
          visible: false
      });
      this.columnsController.columnOption("name", "visible", true);
  
      assert.equal(this.dataController.items().length, 0, "filter is applied");
  });
  
  //T487072
  QUnit.test("Not apply groupInterval of the headerFilter for filterRow", function(assert) {
      //arrange
      this.dataSource = createDataSource([
          { name: "Alex", birthDate: new Date(1992, 7, 6, 12, 30, 21) }
      ]);
      this.applyOptions({
          columns: ["name", {
              dataField: "birthDate",
              dataType: "date",
              headerFilter: {
                  groupInterval: "second"
              },
              filterValue: new Date(1992, 7, 6, 12, 30, 21)
          }]
      });
      this.dataController.setDataSource(this.dataSource);
      this.dataSource.load();
  
      //act, assert
      assert.deepEqual(this.getCombinedFilter(true), [["birthDate", ">=", new Date(1992, 7, 6)], "and", ["birthDate", "<", new Date(1992, 7, 7)]], "filter expression");
  });
  
  QUnit.module("Grouping", { beforeEach: setupModule, afterEach: teardownModule });
  
  //T161732
  QUnit.test("rows when multilevel grouping without key", function(assert) {
      //arrange
      var items = [{
          items: [{
              key: 2,
              items: [
                  { field1: undefined, field2: 2, field3: 3 }, { field1: undefined, field2: 2, field3: 5 },
              ]
          }]
      }];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 2,
          group: ['field1', 'field2']
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, { dataField: 'field2', groupIndex: 1 }, 'field3']
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 4);
      assert.deepEqual(rows[0].values, [undefined]);
      assert.deepEqual(rows[0].groupIndex, 0);
      assert.deepEqual(rows[0].isExpanded, true);
      assert.deepEqual(rows[0].data, items[0]);
      assert.deepEqual(rows[1].values, [undefined, 2]);
      assert.deepEqual(rows[1].groupIndex, 1);
      assert.deepEqual(rows[1].isExpanded, true);
      assert.deepEqual(rows[1].data, items[0].items[0]);
      assert.deepEqual(rows[2].values, [null, null, 3]);
      assert.deepEqual(rows[2].data, { field1: undefined, field2: 2, field3: 3 });
  });
  
  QUnit.test("rows with simple grouping", function(assert) {
      var items = [
          {
              key: 1, items: [
                      { field1: 1, field2: 4 },
                      { field1: 1, field2: 5 }
              ]
          },
          { key: 2, items: [{ field1: 2, field2: 3 }] }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 3,
          group: 'field1'
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2']
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 5);
      assert.deepEqual(rows[0].values, [1]);
      assert.deepEqual(rows[0].groupIndex, 0);
      assert.deepEqual(rows[0].isExpanded, true);
      assert.deepEqual(rows[0].data, items[0]);
      assert.deepEqual(rows[1].values, [null, 4]);
      assert.deepEqual(rows[1].data, { field1: 1, field2: 4 });
      assert.deepEqual(rows[2].values, [null, 5]);
      assert.deepEqual(rows[2].data, { field1: 1, field2: 5 });
      assert.deepEqual(rows[3].values, [2]);
      assert.deepEqual(rows[3].groupIndex, 0);
      assert.deepEqual(rows[3].isExpanded, true);
      assert.deepEqual(rows[3].data, items[1]);
      assert.deepEqual(rows[4].values, [null, 3]);
      assert.deepEqual(rows[4].data, { field1: 2, field2: 3 });
  });
  
  QUnit.test("rows with simple grouping on column with dataType", function(assert) {
      var items = [
          { field1: "1.0", field2: 4 },
          { field1: "1.0", field2: 5 },
          { field1: "2.5", field2: 3 }
      ];
  
      var loadArgs = [];
  
      var dataSource = new DataSource({
          load: function(e) {
              loadArgs.push(e);
              return items;
          },
          totalCount: function() {
              return 3;
          }
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0, dataType: 'number' }, 'field2'],
          remoteOperations: { filtering: true, sorting: true, paging: true },
          grouping: { autoExpandAll: true }
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 5);
      assert.deepEqual(rows[0].values, [1]);
      assert.deepEqual(rows[0].groupIndex, 0);
      assert.deepEqual(rows[0].isExpanded, true);
      assert.deepEqual(rows[0].data, { key: 1, items: [items[0], items[1]] });
      assert.deepEqual(rows[1].values, [null, 4]);
      assert.deepEqual(rows[1].data, items[0]);
      assert.deepEqual(rows[2].values, [null, 5]);
      assert.deepEqual(rows[2].data, items[1]);
      assert.deepEqual(rows[3].values, [2.5]);
      assert.deepEqual(rows[3].groupIndex, 0);
      assert.deepEqual(rows[3].isExpanded, true);
      assert.deepEqual(rows[3].data, { key: 2.5, items: [items[2]] });
      assert.deepEqual(rows[4].values, [null, 3]);
      assert.deepEqual(rows[4].data, items[2]);
  
      //act
      this.dataController.collapseRow([2.5]);
  
      //assert
      assert.equal(loadArgs.length, 3);
      assert.deepEqual(loadArgs[0].filter, undefined);
      assert.deepEqual(loadArgs[1].filter, ["field1", "=", 2.5]);
      assert.deepEqual(loadArgs[1].skip, 0);
      assert.deepEqual(loadArgs[1].take, 1);
      assert.deepEqual(loadArgs[2].filter, ["field1", "<>", 2.5]);
      assert.deepEqual(loadArgs[2].skip, 0);
      assert.deepEqual(loadArgs[2].take, 21);
  });
  
  
  QUnit.test("load options should not contain function selectors when remote grouping enabled", function(assert) {
      var items = [
          { key: "1", count: 2, items: null },
          { key: "2", count: 1, items: null }
      ];
  
      var loadArgs = [];
  
      var dataSource = new DataSource({
          load: function(e) {
              loadArgs.push(e);
              return items;
          },
          totalCount: function() {
              return 3;
          }
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2'],
          remoteOperations: { filtering: true, sorting: true, paging: true, grouping: true },
          grouping: { autoExpandAll: false }
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.dataController.changeRowExpand(["1"]);
  
      //assert
      assert.equal(loadArgs.length, 2);
      assert.deepEqual(loadArgs[0].group, [{ selector: "field1", desc: false, isExpanded: false }]);
      assert.deepEqual(loadArgs[0].filter, undefined);
      assert.deepEqual(loadArgs[0].skip, undefined);
      assert.deepEqual(loadArgs[0].take, undefined);
  
      assert.deepEqual(loadArgs[1].group, null);
      assert.deepEqual(loadArgs[1].filter, ["field1", "=", "1"]);
      assert.deepEqual(loadArgs[1].skip, undefined);
      assert.deepEqual(loadArgs[1].take, undefined);
  });
  
  QUnit.test("rows collapsed group with undefined key", function(assert) {
      var items = [
          {
              key: undefined, items: null
          },
          { key: 2, items: [{ field1: 2, field2: 3 }] }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 3,
          group: 'field1'
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2']
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 3);
      assert.deepEqual(rows[0].values, [undefined]);
      assert.deepEqual(rows[0].groupIndex, 0);
      assert.deepEqual(rows[0].isExpanded, false);
      assert.deepEqual(rows[0].data, items[0]);
      assert.deepEqual(rows[1].values, [2]);
      assert.deepEqual(rows[1].groupIndex, 0);
      assert.deepEqual(rows[1].isExpanded, true);
      assert.deepEqual(rows[1].data, items[1]);
      assert.deepEqual(rows[2].values, [null, 3]);
      assert.deepEqual(rows[2].data, { field1: 2, field2: 3 });
  });
  
  QUnit.test("rows with multilevel grouping", function(assert) {
      var items = [{
          key: 1, items: [
              {
                  key: 2, items: [
                          { field1: 1, field2: 2, field3: 4 }, { field1: 1, field2: 2, field3: 5 }
                  ]
              }
          ]
      }];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 2,
          group: ['field1', 'field2']
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, { dataField: 'field2', groupIndex: 1 }, 'field3']
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 4);
      assert.deepEqual(rows[0].values, [1]);
      assert.deepEqual(rows[0].groupIndex, 0);
      assert.deepEqual(rows[0].isExpanded, true);
      assert.deepEqual(rows[0].data, items[0]);
      assert.deepEqual(rows[1].values, [1, 2]);
      assert.deepEqual(rows[1].groupIndex, 1);
      assert.deepEqual(rows[1].isExpanded, true);
      assert.deepEqual(rows[1].data, items[0].items[0]);
      assert.deepEqual(rows[2].values, [null, null, 4]);
      assert.deepEqual(rows[2].data, { field1: 1, field2: 2, field3: 4 });
      assert.deepEqual(rows[3].values, [null, null, 5]);
      assert.deepEqual(rows[3].data, { field1: 1, field2: 2, field3: 5 });
  });
  
  QUnit.test("collapsed group", function(assert) {
      var items = [
          {
              key: 1, items: null
          },
          { key: 2, items: [{ field1: 2, field2: 3 }] }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 2,
          group: 'field1'
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2']
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 3);
      assert.deepEqual(rows[0].values, [1]);
      assert.deepEqual(rows[0].groupIndex, 0);
      assert.deepEqual(rows[0].isExpanded, false);
      assert.deepEqual(rows[0].data, {
          key: 1, items: null
      });
      assert.deepEqual(rows[1].values, [2]);
      assert.deepEqual(rows[1].groupIndex, 0);
      assert.deepEqual(rows[1].isExpanded, true);
      assert.deepEqual(rows[1].data, {
          key: 2, items: [
                  { field1: 2, field2: 3 }
          ]
      });
      assert.deepEqual(rows[2].values, [null, 3]);
      assert.deepEqual(rows[2].data, { field1: 2, field2: 3 });
  });
  
  QUnit.test("continue group", function(assert) {
      var items = [
          {
              key: 1, isContinuation: true, items: [
                      { field1: 1, field2: 4 },
                      { field1: 1, field2: 5 }
              ]
          },
          { key: 2, items: [{ field1: 2, field2: 3 }] }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 3,
          group: 'field1'
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2']
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 5);
      assert.deepEqual(rows[0].values, [1]);
      assert.deepEqual(rows[0].data, items[0]);
      assert.deepEqual(rows[1].values, [null, 4]);
      assert.deepEqual(rows[1].data, { field1: 1, field2: 4 });
      assert.deepEqual(rows[2].values, [null, 5]);
      assert.deepEqual(rows[2].data, { field1: 1, field2: 5 });
      assert.deepEqual(rows[3].values, [2]);
      assert.deepEqual(rows[3].groupIndex, 0);
      assert.deepEqual(rows[3].isExpanded, true);
      assert.deepEqual(rows[4].values, [null, 3]);
      assert.deepEqual(rows[4].data, { field1: 2, field2: 3 });
  });
  
  QUnit.test("continue group when virtual scrolling", function(assert) {
      var items = [
          {
              key: 1, isContinuation: true, items: [
                      { field1: 1, field2: 4 },
                      { field1: 1, field2: 5 }
              ]
          },
          { key: 2, items: [{ field1: 2, field2: 3 }] }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 3,
          group: 'field1'
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2'],
          scrolling: { mode: 'virtual' }
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 4);
      assert.deepEqual(rows[0].values, [null, 4]);
      assert.deepEqual(rows[0].data, { field1: 1, field2: 4 });
      assert.deepEqual(rows[1].values, [null, 5]);
      assert.deepEqual(rows[1].data, { field1: 1, field2: 5 });
      assert.deepEqual(rows[2].values, [2]);
      assert.deepEqual(rows[2].groupIndex, 0);
      assert.deepEqual(rows[2].isExpanded, true);
      assert.deepEqual(rows[3].values, [null, 3]);
      assert.deepEqual(rows[3].data, { field1: 2, field2: 3 });
  });
  
  QUnit.test("continue group when infinite scrolling", function(assert) {
      var items = [
          {
              key: 1, isContinuation: true, items: [
                      { field1: 1, field2: 4 },
                      { field1: 1, field2: 5 }
              ]
          },
          { key: 2, items: [{ field1: 2, field2: 3 }] }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 3,
          group: 'field1'
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2'],
          scrolling: { mode: 'infinite' }
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 4);
      assert.deepEqual(rows[0].values, [null, 4]);
      assert.deepEqual(rows[0].data, { field1: 1, field2: 4 });
      assert.deepEqual(rows[1].values, [null, 5]);
      assert.deepEqual(rows[1].data, { field1: 1, field2: 5 });
      assert.deepEqual(rows[2].values, [2]);
      assert.deepEqual(rows[2].groupIndex, 0);
      assert.deepEqual(rows[2].isExpanded, true);
      assert.deepEqual(rows[3].values, [null, 3]);
      assert.deepEqual(rows[3].data, { field1: 2, field2: 3 });
  });
  
  QUnit.test("continue multilevel group", function(assert) {
      var items = [{
          key: 1, isContinuation: true, items: [
              {
                  key: 2, isContinuation: true, items: [
                          { field1: 1, field2: 2, field3: 4 }, { field1: 1, field2: 2, field3: 5 }
                  ]
              }
          ]
      },
      {
          key: 2, items: [
                          { key: 1, items: [{ field1: 2, field2: 1, field3: 3 }] }
          ]
      }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 3,
          group: ['field1', 'field2']
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, { dataField: 'field2', groupIndex: 1 }, 'field3']
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 7);
      assert.deepEqual(rows[0].values, [1]);
      assert.deepEqual(rows[0].data, items[0]);
      assert.deepEqual(rows[1].values, [1, 2]);
      assert.deepEqual(rows[1].data, items[0].items[0]);
      assert.deepEqual(rows[2].values, [null, null, 4]);
      assert.deepEqual(rows[2].data, { field1: 1, field2: 2, field3: 4 });
      assert.deepEqual(rows[3].values, [null, null, 5]);
      assert.deepEqual(rows[3].data, { field1: 1, field2: 2, field3: 5 });
      assert.deepEqual(rows[4].values, [2]);
      assert.deepEqual(rows[4].groupIndex, 0);
      assert.deepEqual(rows[4].isExpanded, true);
      assert.deepEqual(rows[5].values, [2, 1]);
      assert.deepEqual(rows[5].groupIndex, 1);
      assert.deepEqual(rows[5].isExpanded, true);
      assert.deepEqual(rows[6].values, [null, null, 3]);
      assert.deepEqual(rows[6].data, { field1: 2, field2: 1, field3: 3 });
  });
  
  QUnit.test("continue multilevel group when virtual scrolling", function(assert) {
      var items = [{
          key: 1, isContinuation: true, items: [
              {
                  key: 2, isContinuation: true, items: [
                          { field1: 1, field2: 2, field3: 4 }, { field1: 1, field2: 2, field3: 5 }
                  ]
              }
          ]
      },
      {
          key: 2, items: [
                          { key: 1, items: [{ field1: 2, field2: 1, field3: 3 }] }
          ]
      }
      ];
  
      var dataSource = new MockGridDataSource({
          items: items,
          itemsCount: 3,
          group: ['field1', 'field2']
      });
  
      this.applyOptions({
          columns: [{ dataField: 'field1', groupIndex: 0 }, { dataField: 'field2', groupIndex: 1 }, 'field3'],
          scrolling: { mode: 'virtual' }
      });
  
      //act
      this.dataController.setDataSource(dataSource);
      dataSource.load();
      var rows = this.dataController.items();
  
      //assert
      assert.equal(rows.length, 5);
      assert.deepEqual(rows[0].values, [null, null, 4]);
      assert.deepEqual(rows[0].data, { field1: 1, field2: 2, field3: 4 });
      assert.deepEqual(rows[1].values, [null, null, 5]);
      assert.deepEqual(rows[1].data, { field1: 1, field2: 2, field3: 5 });
      assert.deepEqual(rows[2].values, [2]);
      assert.deepEqual(rows[2].groupIndex, 0);
      assert.deepEqual(rows[2].isExpanded, true);
      assert.deepEqual(rows[3].values, [2, 1]);
      assert.deepEqual(rows[3].groupIndex, 1);
      assert.deepEqual(rows[3].isExpanded, true);
      assert.deepEqual(rows[4].values, [null, null, 3]);
      assert.deepEqual(rows[4].data, { field1: 2, field2: 1, field3: 3 });
  });
  
  QUnit.test("changeRowExpand", function(assert) {
      var countCallPageChanged = 0,
          dataSourceOptions = {
              items: [{ key: 1, items: [] }],
              itemsCount: 1
          };
  
      this.dataController.setDataSource(new MockGridDataSource(dataSourceOptions));
  
      this.dataController.pageChanged.add(function() {
          countCallPageChanged++;
      });
  
      //act
      this.dataController.changeRowExpand([1]);
  
      //assert
      assert.ok(dataSourceOptions.loaded);
  
      //T262949
      assert.equal(countCallPageChanged, 0, "pageChanged is not called");
  });
  
  QUnit.test("collapseAll", function(assert) {
      var dataSourceOptions = {
          items: [{}],
          itemsCount: 1,
          pageIndex: 1
      };
  
      this.dataController.setDataSource(new MockGridDataSource(dataSourceOptions));
  
      //act
      this.dataController.collapseAll(1);
  
      //assert
      assert.strictEqual(dataSourceOptions.pageIndex, 0);
      assert.ok(!dataSourceOptions.reloaded);
  });
  
  QUnit.test("expandAll", function(assert) {
      var dataSourceOptions = {
          items: [{}],
          itemsCount: 1,
          pageIndex: 1
      };
  
      this.dataController.setDataSource(new MockGridDataSource(dataSourceOptions));
  
      //act
      this.dataController.expandAll(1);
  
      //assert
      assert.strictEqual(dataSourceOptions.pageIndex, 0);
      assert.ok(!dataSourceOptions.reloaded);
  });
  
  QUnit.module("Editing", { beforeEach: setupModule, afterEach: teardownModule });
  
  QUnit.test("Inserting Row", function(assert) {
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      //act
      this.editingController.addRow();
  
      //assert
      assert.equal(this.dataController.items().length, 3);
  
      assert.deepEqual(this.dataController.items()[0].values, [undefined, undefined]);
      assert.deepEqual(this.dataController.items()[0].data, {});
      assert.ok(!commonUtils.isDefined(this.dataController.items()[0].dataIndex));
  
      assert.deepEqual(this.dataController.items()[1].values, ['Alex', '55-55-55']);
      assert.deepEqual(this.dataController.items()[1].data, { name: 'Alex', phone: '55-55-55' });
      assert.equal(this.dataController.items()[1].dataIndex, 0);
  });
  
  //T327787, T333894
  QUnit.test("Inserting Row for grouped data", function(assert) {
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' }, { group: "name" });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.expandAll();
  
      //act
      this.editingController.addRow();
  
      //assert
      assert.equal(this.dataController.items().length, 5);
  
      assert.deepEqual(this.dataController.items()[0].values, [null, undefined], "item 0 values");
      assert.deepEqual(this.dataController.items()[0].data, {}, "item 0 data");
      assert.deepEqual(this.dataController.items()[0].rowType, "data", "item 0 rowType");
  
      assert.deepEqual(this.dataController.items()[1].values, ['Alex'], "item 1 values");
      assert.deepEqual(this.dataController.items()[1].data, { key: 'Alex', items: [{ name: 'Alex', phone: '55-55-55' }] }, "item 1 data");
      assert.deepEqual(this.dataController.items()[1].rowType, "group", "item 1 rowType");
  
      assert.deepEqual(this.dataController.items()[2].values, [null, '55-55-55'], "item 2 values");
      assert.deepEqual(this.dataController.items()[2].data, { name: 'Alex', phone: '55-55-55' }, "item 2 data");
      assert.deepEqual(this.dataController.items()[2].rowType, "data", "item 2 rowType");
  });
  
  QUnit.test("Double inserting Row", function(assert) {
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.editingController.addRow();
      //act
      this.editingController.addRow();
  
      //assert
      assert.equal(this.dataController.items().length, 3);
  
      assert.deepEqual(this.dataController.items()[0].values, [undefined, undefined]);
      assert.deepEqual(this.dataController.items()[0].data, {});
  
      assert.deepEqual(this.dataController.items()[1].values, ['Alex', '55-55-55']);
      assert.deepEqual(this.dataController.items()[1].data, { name: 'Alex', phone: '55-55-55' });
  });
  
  QUnit.test("Cancel Inserting Row", function(assert) {
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.editingController.addRow();
      //act
      this.editingController.cancelEditData();
  
      //assert
      assert.equal(this.dataController.items().length, 2);
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', '55-55-55']);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Alex', phone: '55-55-55' });
  });
  
  QUnit.test("Cancel Inserting Row after change page", function(assert) {
      var array = [
          { name: 'Alex', phone: '55-55-55' },
          { name: 'Dan', phone: '98-75-21' },
          { name: 'Martin', phone: '12-34-56' }
      ];
  
      var dataSource = createDataSource(array, { key: 'name' }, { pageSize: 2 });
  
      this.dataController.setDataSource(dataSource);
      dataSource.load();
  
      this.editingController.addRow();
      //act
      this.dataController.pageIndex(1);
  
      //assert
      assert.ok(!this.editingController.isEditing());
      assert.equal(this.dataController.items().length, 1);
  
      assert.deepEqual(this.dataController.items()[0].data, array[2]);
  });
  
  QUnit.module("Error handling", {
      beforeEach: function() {
          this.clock = sinon.useFakeTimers();
      },
      afterEach: function() {
          this.clock.restore();
      }
  });
  
  QUnit.test("load error", function(assert) {
      var dataErrors = [],
          callbackDataErrors = [];
  
      this.options = {
          loadingTimeout: 0,
          dataSource: {
              load: function() {
                  return $.Deferred().reject('Load error');
              },
              totalCount: function() {
                  return $.Deferred().reject('Total count error');
              }
          },
          onDataErrorOccurred: function(e) {
              dataErrors.push(e.error.message);
          }
      };
  
      //act
      setupDataGridModules(this, ['data', 'columns']);
  
      this.dataController.dataErrorOccurred.add(function(error) {
          callbackDataErrors.push(error.message);
      });
      this.clock.tick();
  
      //assert
      assert.deepEqual(dataErrors, ['Load error']);
      assert.deepEqual(callbackDataErrors, ['Load error']);
  });
  
  QUnit.test("return false on dataErrorOccurred", function(assert) {
      var callbackDataErrors = [],
          dataErrors = [];
  
      this.options = {
          loadingTimeout: 0,
          dataSource: {
              load: function() {
                  return $.Deferred().reject('Load error');
              },
              totalCount: function() {
                  return $.Deferred().reject('Total count error');
              }
          },
          onDataErrorOccurred: function(e) {
              dataErrors.push(e.error.message);
              return false;
          }
      };
  
      //act
      setupDataGridModules(this, ['data', 'columns']);
  
      this.dataController.dataErrorOccurred.add(function(error) {
          callbackDataErrors.push(error.message);
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(dataErrors, ['Load error']);
      assert.deepEqual(callbackDataErrors, []);
  });
  
  
  QUnit.test("insert error", function(assert) {
      var dataErrors = [];
  
      this.options = {
          dataSource: {
              key: 'field1',
              load: function() {
                  return [{ field1: 1, field2: 2 }];
              },
              totalCount: function() {
                  return 1;
              },
              insert: function() {
                  return $.Deferred().reject('field1 and field2 required');
              }
          },
          onDataErrorOccurred: function(e) {
              dataErrors.push(e.error.message);
          }
      };
  
      //act
      setupDataGridModules(this, ['data', 'columns', 'editing']);
  
      this.editingController.getFirstEditableCellInRow = function() { return $([]); };
  
      this.clock.tick();
  
      //act
      this.editingController.addRow();
      this.editingController.saveEditData();
      //assert
      assert.deepEqual(dataErrors, ['field1 and field2 required']);
  });
  
  QUnit.test("remove error", function(assert) {
      var dataErrors = [];
  
      this.options = {
          dataSource: {
              key: 'field1',
              load: function() {
                  return [{ field1: 1, field2: 2 }];
              },
              totalCount: function() {
                  return 1;
              },
              remove: function() {
                  return $.Deferred().reject('Remove error');
              }
          },
          onDataErrorOccurred: function(e) {
              dataErrors.push(e.error.message);
          },
          editing: {
              texts: {
                  confirmDeleteMessage: ''
              }
          }
      };
  
      //act
      setupDataGridModules(this, ['data', 'columns', 'editing']);
  
      this.clock.tick();
  
      //act
      this.editingController.deleteRow(0);
  
      //assert
      assert.deepEqual(dataErrors, ['Remove error']);
  });
  
  QUnit.test("update error", function(assert) {
      var dataErrors = [];
  
      this.options = {
          columns: ["field1", { setCellValue: function(data, value) { data.field1 = value; }, allowEditing: true }],
          dataSource: {
              key: 'field1',
              load: function() {
                  return [{ field1: 1, field2: 2 }];
              },
              totalCount: function() {
                  return 1;
              },
              update: function() {
                  return $.Deferred().reject('Update error');
              }
          },
          onDataErrorOccurred: function(e) {
              dataErrors.push(e.error.message);
          }
      };
  
      //act
      setupDataGridModules(this, ['data', 'columns', 'editing']);
  
      this.editingController.getFirstEditableCellInRow = function() { return $([]); };
  
      this.clock.tick();
  
      //act
      this.editingController.editRow(0);
      this.editingController.updateFieldValue({
          key: 1,
          column: {
              setCellValue: function(data, value) { data.field1 = value; }
          },
          value: 4
      });
      this.editingController.saveEditData();
  
      //assert
      assert.equal(this.editingController._editRowIndex, 0, "edit row index");
      assert.deepEqual(dataErrors, ['Update error']);
  });
  
  QUnit.module("Remote Grouping", {
      beforeEach: function() {
          this.clock = sinon.useFakeTimers();
          this.setupDataGridModules = function() {
              setupDataGridModules(this, ['data', 'columns', 'filterRow', 'grouping', 'summary']);
          };
      },
      afterEach: function() {
          this.clock.restore();
      }
  });
  
  //T320744
  QUnit.test("Exception when CustomStore returns plain data and remote grouping enabled", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Dan', age: 25 }
                  ], {
                      totalCount: 10
                  });
              },
              pageSize: 2
          },
          paging: {
              enabled: true
          },
          columns: ["name", "age"],
          remoteOperations: true
      };
  
      //act
      try {
          this.setupDataGridModules();
      } catch(e) {
          //assert
          assert.ok(e instanceof Error, "data error is Error object");
  
          assert.equal(e.__id, "E1037", "data error id");
          assert.equal(e.__details, "Invalid structure of grouped data", "data error details");
      }
  
      assert.strictEqual(storeLoadOptions.skip, undefined, "no skip option");
      assert.strictEqual(storeLoadOptions.take, undefined, "no take option");
      assert.deepEqual(storeLoadOptions.group, [{ selector: "name", desc: false, isExpanded: false }], "group option");
  
      assert.ok(this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), -1, "totalCount");
      assert.equal(this.dataController.pageCount(), 1, "pageCount");
  });
  
  //T366766
  QUnit.test("Exception when CustomStore returns groups without items", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { key: 'Alex', count: 1 },
                      { key: 'Dan', count: 1 }
                  ], {
                      totalCount: 10
                  });
              },
              pageSize: 2
          },
          paging: {
              enabled: true
          },
          columns: ["name", "age"],
          remoteOperations: true
      };
  
      //act
      try {
          this.setupDataGridModules();
      } catch(e) {
          //assert
          assert.ok(e instanceof Error, "data error is Error object");
  
          assert.equal(e.__id, "E1037", "data error id");
          assert.equal(e.__details, "Invalid structure of grouped data", "data error details");
      }
  
      assert.strictEqual(storeLoadOptions.skip, undefined, "no skip option");
      assert.strictEqual(storeLoadOptions.take, undefined, "no take option");
      assert.deepEqual(storeLoadOptions.group, [{ selector: "name", desc: false, isExpanded: false }], "group option");
  
      assert.ok(this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), -1, "totalCount");
      assert.equal(this.dataController.pageCount(), 1, "pageCount");
  });
  
  //T317797
  QUnit.test("CustomStore load options when remote grouping and paging enabled and no groups", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Dan', age: 25 }
                  ], {
                      totalCount: 10
                  });
              },
              pageSize: 2
          },
          paging: {
              enabled: true
          },
          columns: ["name", "age"],
          remoteOperations: {
              grouping: true,
              paging: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!storeLoadOptions.group, "no group option");
      assert.strictEqual(storeLoadOptions.skip, 0, "skip option");
      assert.strictEqual(storeLoadOptions.take, 2, "take option");
      assert.strictEqual(storeLoadOptions.requireTotalCount, true, "requireTotalCount option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 10, "totalCount");
      assert.equal(this.dataController.hasKnownLastPage(), true, "hasKnownLastPage");
      assert.equal(this.dataController.items().length, 2, "items count");
      assert.equal(this.dataController.pageCount(), 5, "pageCount");
  });
  
  //T318309
  QUnit.test("CustomStore load options when remote grouping by column with serialization format", function(assert) {
      this.options = {
          dataSource: {
              group: "birthDate",
              load: function(options) {
                  return $.Deferred().resolve([
                      { key: '1980/10/15', items: [{ name: 'Alex', birthDate: '1980/10/15' }] },
                      { key: '1984/1/1', items: [{ name: 'Dan', age: '1984/1/1' }] }
                  ]);
              },
              pageSize: 2
          },
          columns: ["name", { dataField: "birthDate", dataType: "date" }],
          remoteOperations: {
              grouping: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.totalCount(), 2, "totalCount");
      assert.equal(this.dataController.pageCount(), 1, "pageCount");
      assert.deepEqual(this.dataController.items()[0].rowType, "group", "item 1 rowType");
      assert.deepEqual(this.dataController.items()[0].key, ['1980/10/15'], "item 1 key");
      assert.deepEqual(this.dataController.items()[0].values, [new Date('1980/10/15')], "item 1 values");
      assert.deepEqual(this.dataController.items()[1].rowType, "group", "item 2 rowType");
      assert.deepEqual(this.dataController.items()[1].key, ['1984/1/1'], "item 2 key");
      assert.deepEqual(this.dataController.items()[1].values, [new Date('1984/1/1')], "item 2 values");
  });
  
  QUnit.module("Summary", {
      beforeEach: function() {
          this.clock = sinon.useFakeTimers();
          this.setupDataGridModules = function(options) {
              setupDataGridModules(this, ['data', 'columns', 'filterRow', 'grouping', 'summary'], options);
          };
      },
      afterEach: function() {
          this.clock.restore();
      }
  });
  
  QUnit.test("No total summary items", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ]
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), []);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("CustomStore load options when remoteOperations auto and summary exists", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return [
                      { name: 'Alex', age: 19 },
                      { name: 'Dan', age: 25 }
                  ];
              }
          },
          summary: {
              groupItems: [{
                  summaryType: "count"
              }],
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }]
          },
          remoteOperations: 'auto'
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!storeLoadOptions.skip && !storeLoadOptions.take, "no paging options");
      assert.ok(!storeLoadOptions.summary, "no summary options");
      assert.ok(!this.dataController.isLoading());
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 19,
              column: 'age',
              summaryType: 'min'
          }]]
      }], "totalFooter items");
      assert.deepEqual(this.dataController.items()[0].rowType, "group", "first row type");
      //T328430
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[{ summaryType: "count", value: 1 }], []], "group summaryCells");
  });
  
  QUnit.test("CustomStore load options when remote summary enabled", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Dan', age: 25 }
                  ], {
                      totalCount: 10,
                      summary: [3]
                  });
              },
              pageSize: 2
          },
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'custom'
              }]
          },
          remoteOperations: {
              paging: true,
              summary: true
          }
      };
  
  
      var errorId;
  
      errors.log = function(id) {
          if(!errorId) {
              errorId = id;
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!errorId, "no errors");
      assert.strictEqual(storeLoadOptions.skip, 0, "skip option");
      assert.strictEqual(storeLoadOptions.take, 2, "take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "custom" }], "totalSummary option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 10, "totalCount");
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 3,
              column: 'age',
              summaryType: 'custom'
          }]]
      }], "footerItems");
  });
  
  QUnit.test("CustomStore load options when all remoteOperations enabled and summary is defined", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Dan', age: 25 }
                  ], {
                      totalCount: 10,
                      summary: [3]
                  });
              },
              pageSize: 2
          },
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'custom'
              }]
          },
          remoteOperations: true
      };
  
  
      var errorId;
  
      errors.log = function(id) {
          if(!errorId) {
              errorId = id;
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!errorId, "no errors");
      assert.strictEqual(storeLoadOptions.skip, 0, "skip option");
      assert.strictEqual(storeLoadOptions.take, 2, "take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "custom" }], "totalSummary option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 10, "totalCount");
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 3,
              column: 'age',
              summaryType: 'custom'
          }]]
      }], "footerItems");
  });
  
  //T306309
  QUnit.test("CustomStore load options when remote summary enabled and summary is not returned", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Dan', age: 25 }
                  ], {
                      totalCount: 10
                  });
              },
              pageSize: 2
          },
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }]
          },
          remoteOperations: {
              paging: true,
              summary: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(storeLoadOptions.skip, 0, "skip option");
      assert.strictEqual(storeLoadOptions.take, 2, "take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "min" }], "summary totalItems option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 10, "totalCount");
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              column: 'age',
              summaryType: 'min'
          }]]
      }], "footerItems");
  });
  
  
  //T353923
  QUnit.test("CustomStore load options when remote paging/summary enabled and grouping is defined with summary.groupItems", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Alex', age: 18 },
                      { name: 'Dan', age: 30 },
                      { name: 'Dan', age: 25 }
                  ], {
                      totalCount: 10,
                      summary: [5]
                  });
              },
              pageSize: 2
          },
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }],
              groupItems: [{
                  column: 'age',
                  summaryType: 'max'
              }]
          },
          paging: {
              enabled: true
          },
          remoteOperations: {
              paging: true,
              summary: true
          },
          grouping: {
              autoExpandAll: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(storeLoadOptions.skip, undefined, "skip option");
      assert.strictEqual(storeLoadOptions.take, undefined, "take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "min" }], "summary totalItems option");
      assert.deepEqual(storeLoadOptions.groupSummary, undefined, "summary groupItems option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 4, "totalCount");
      assert.equal(this.dataController.pageCount(), 4, "pageCount");
      assert.deepEqual(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].data, {
          key: "Alex",
          isContinuationOnNextPage: true,
          items: [{ name: 'Alex', age: 19 }],
          aggregates: [19]
      });
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[{
          column: 'age',
          columnCaption: "Age",
          summaryType: 'max',
          value: 19
      }], []]);
      assert.deepEqual(this.dataController.items()[1].data, { name: 'Alex', age: 19 });
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              column: 'age',
              summaryType: 'min',
              value: 5
          }]]
      }], "footerItems");
  });
  
  //T353923
  QUnit.test("CustomStore load options when remote paging/summary enabled and grouping is defined without summary.groupitems", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Alex', age: 18 },
                      { name: 'Dan', age: 30 },
                      { name: 'Dan', age: 25 }
                  ], {
                      totalCount: 10,
                      summary: [5]
                  });
              },
              pageSize: 2
          },
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }]
          },
          paging: {
              enabled: true
          },
          remoteOperations: {
              paging: true,
              summary: true
          },
          grouping: {
              autoExpandAll: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(storeLoadOptions.skip, undefined, "skip option");
      assert.strictEqual(storeLoadOptions.take, undefined, "take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "min" }], "summary totalItems option");
      assert.deepEqual(storeLoadOptions.groupSummary, undefined, "summary groupItems option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 4, "totalCount");
      assert.equal(this.dataController.pageCount(), 4, "pageCount");
      assert.deepEqual(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].data, {
          key: "Alex",
          isContinuationOnNextPage: true,
          items: [{ name: 'Alex', age: 19 }]
      });
      assert.deepEqual(this.dataController.items()[1].data, { name: 'Alex', age: 19 });
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              column: 'age',
              summaryType: 'min',
              value: 5
          }]]
      }], "footerItems");
  });
  
  
  //T353923
  QUnit.test("CustomStore load options when remote paging/sorting/filtering/summary enabled and grouping is defined without summary.groupitems", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19 },
                      { name: 'Alex', age: 18 },
                      { name: 'Dan', age: 30 }
                  ], {
                      totalCount: 10,
                      summary: [5]
                  });
              },
              pageSize: 2
          },
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }]
          },
          paging: {
              enabled: true
          },
          remoteOperations: {
              paging: true,
              sorting: true,
              filtering: true,
              summary: true
          },
          grouping: {
              autoExpandAll: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(storeLoadOptions.skip, 0, "skip option");
      assert.strictEqual(storeLoadOptions.take, 3, "take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "min" }], "summary totalItems option");
      assert.deepEqual(storeLoadOptions.groupSummary, undefined, "summary groupItems option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 10, "totalCount");
      assert.equal(this.dataController.pageCount(), 5, "pageCount");
      assert.deepEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data, {
          key: "Alex",
          items: [{ name: 'Alex', age: 19 }, { name: 'Alex', age: 18 }]
      });
      assert.deepEqual(this.dataController.items()[1].data, { name: 'Alex', age: 19 });
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              column: 'age',
              summaryType: 'min',
              value: 5
          }]]
      }], "footerItems");
  });
  
  //T306309, T368811
  QUnit.test("CustomStore load options when remote summary enabled and summary is returned as strings", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { name: 'Alex', age: 19, date: "2013-05-04T00:00:00Z" },
                      { name: 'Dan', age: 25, date: "2014-11-30T00:00:00Z" }
                  ], {
                      totalCount: 10,
                      summary: ["3", "2014-11-30T00:00:00Z"]
                  });
              },
              pageSize: 2
          },
          columns: ["name", "age", { dataField: "date", dataType: "date" }],
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }, {
                  column: 'date',
                  summaryType: 'max'
              }]
          },
          remoteOperations: {
              paging: true,
              summary: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(storeLoadOptions.skip, 0, "skip option");
      assert.strictEqual(storeLoadOptions.take, 2, "take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "min" }, { selector: "date", summaryType: "max" }], "summary totalItems option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 10, "totalCount");
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 3,
              column: 'age',
              summaryType: 'min'
          }], [{
              value: new Date("2014-11-30T00:00:00Z"),
              valueFormat: "shortDate",
              column: 'date',
              summaryType: 'max'
          }]]
      }], "footerItems");
  });
  
  
  QUnit.test("CustomStore load options when remote summary and remote grouping without paging enabled", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { key: 'Alex', items: [{ name: 'Alex', age: 19 }], summary: [1] },
                      { key: 'Dan', items: [{ name: 'Dan', age: 25 }], summary: [2] }
                  ], {
                      summary: [3]
                  });
              },
              pageSize: 2
          },
          paging: {
              enabled: true
          },
          columns: ["name", "age"],
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }],
              groupItems: [{
                  column: 'age',
                  summaryType: 'count'
              }]
          },
          remoteOperations: {
              grouping: true,
              summary: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(storeLoadOptions.skip, undefined, "no skip option");
      assert.strictEqual(storeLoadOptions.take, undefined, "no take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "min" }], "summary totalItems option");
      assert.deepEqual(storeLoadOptions.groupSummary, [{ selector: "age", summaryType: "count" }], "summary groupItems option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 2, "totalCount");
      assert.equal(this.dataController.pageCount(), 1, "pageCount");
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[{
          column: "age",
          columnCaption: "Age",
          summaryType: "count",
          value: 1
      }], []], "summary cells");
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 3,
              column: 'age',
              summaryType: 'min'
          }]]
      }], "footerItems");
  });
  
  QUnit.test("CustomStore load options when remote summary and remote grouping and paging enabled", function(assert) {
      var storeLoadOptions;
      this.options = {
          dataSource: {
              group: "name",
              load: function(options) {
                  storeLoadOptions = options;
                  return $.Deferred().resolve([
                      { key: 'Alex', items: [{ name: 'Alex', age: 19 }], summary: [1] },
                      { key: 'Dan', items: [{ name: 'Dan', age: 25 }], summary: [2] }
                  ], {
                      totalCount: 10,
                      summary: [3]
                  });
              },
              pageSize: 2
          },
          paging: {
              enabled: true
          },
          columns: ["name", "age"],
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }],
              groupItems: [{
                  column: 'age',
                  summaryType: 'count'
              }]
          },
          remoteOperations: {
              grouping: true,
              paging: true,
              summary: true
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(storeLoadOptions.skip, undefined, "no skip option");
      assert.strictEqual(storeLoadOptions.take, undefined, "no take option");
      assert.deepEqual(storeLoadOptions.totalSummary, [{ selector: "age", summaryType: "min" }], "summary totalItems option");
      assert.deepEqual(storeLoadOptions.groupSummary, [{ selector: "age", summaryType: "count" }], "summary groupItems option");
      assert.ok(!this.dataController.isLoading());
      assert.equal(this.dataController.totalCount(), 2, "totalCount");
      assert.equal(this.dataController.pageCount(), 1, "pageCount");
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[{
          column: "age",
          columnCaption: "Age",
          summaryType: "count",
          value: 1
      }], []], "summary cells");
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 3,
              column: 'age',
              summaryType: 'min'
          }]]
      }], "footerItems");
  });
  
  QUnit.test("One total summary item for second column", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 19,
              column: 'age',
              summaryType: 'min'
          }]]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("Filtering on column with dataType when summary is defined", function(assert) {
      this.options = {
          columns: ["name", { dataField: "birthDate", dataType: "date", filterValue: new Date('1987/5/5') }],
          dataSource: [
              { name: 'Alex', birthDate: '1987/5/5' },
              { name: 'Dan', birthDate: '1985/3/21' }
          ],
          remoteOperations: "auto",
          summary: {
              totalItems: [{
                  showInColumn: 'birthDate',
                  summaryType: 'count'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 1);
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 1,
              showInColumn: 'birthDate',
              summaryType: 'count'
          }]]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("Total summary items when number in string format", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: '19' },
              { name: 'Dan', age: '25' }
          ],
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'avg'
              },
              {
                  column: 'age',
                  summaryType: 'sum'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 22,
              column: 'age',
              summaryType: 'avg'
          }, {
              value: 44,
              column: 'age',
              summaryType: 'sum'
          }]
          ]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("Several total summary items in one column", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'age',
                  summaryType: 'min'
              },
              {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 19,
              column: 'age',
              summaryType: 'min'
          },
          {
              value: 25,
              column: 'age',
              summaryType: 'max'
          }]
          ]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  //T351922
  QUnit.test("skip empty values for total summary items", function(assert) {
      //act
      this.setupDataGridModules({
          initDefaultOptions: true,
          options: {
              dataSource: [
                  { name: 'Alex', age: 19 },
                  { name: 'Dan', age: 25 },
                  { name: 'Max', age: null },
                  { name: 'Bob', age: null }
              ],
              summary: {
                  totalItems: [{
                      column: 'age',
                      summaryType: 'avg'
                  },
                  {
                      column: 'age',
                      summaryType: 'avg',
                      skipEmptyValues: false
                  }]
              }
          }
      });
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 22,
              column: 'age',
              summaryType: 'avg'
          },
          {
              value: 11,
              column: 'age',
              summaryType: 'avg',
              skipEmptyValues: false
          }]
          ]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  //T351922
  QUnit.test("skip empty values for total summary items when common skipEmptyValues false", function(assert) {
      //act
      this.setupDataGridModules({
          initDefaultOptions: true,
          options: {
              dataSource: [
                  { name: 'Alex', age: 19 },
                  { name: 'Dan', age: 25 },
                  { name: 'Max', age: null },
                  { name: 'Bob', age: null }
              ],
              summary: {
                  skipEmptyValues: false,
                  totalItems: [{
                      column: 'age',
                      summaryType: 'avg'
                  },
                  {
                      column: 'age',
                      summaryType: 'avg',
                      skipEmptyValues: true
                  }]
              }
          }
      });
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 11,
              column: 'age',
              summaryType: 'avg'
          },
          {
              value: 22,
              column: 'age',
              summaryType: 'avg',
              skipEmptyValues: true
          }]
          ]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("total summary item show in other column", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'age',
                  showInColumn: 'name',
                  summaryType: 'min'
              },
              {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[{
              value: 19,
              column: 'age',
              columnCaption: 'Age',
              showInColumn: 'name',
              summaryType: 'min'
          }], [{
              value: 25,
              column: 'age',
              summaryType: 'max'
          }]]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("total summary for column with showWhenGrouped", function(assert) {
      this.options = {
          columns: [{ dataField: 'name', showWhenGrouped: true, groupIndex: 0 }, 'age'],
          dataSource: {
              group: "name",
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Dan', age: 25 }
              ]
          },
          summary: {
              totalItems: [{
                  column: 'age',
                  showInColumn: 'name',
                  summaryType: 'min'
              },
              {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[{
              column: "age",
              columnCaption: "Age",
              showInColumn: "name",
              summaryType: "min",
              value: 19
          }], [{
              value: 19,
              column: 'age',
              columnCaption: 'Age',
              showInColumn: 'name',
              summaryType: 'min'
          }], [{
              value: 25,
              column: 'age',
              summaryType: 'max'
          }]]
      }]);
  });
  
  QUnit.test("total summary for one summary item if it has group index", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: "name",
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Dan', age: 25 }
              ]
          },
          summary: {
              totalItems: [{
                  column: 'name',
                  summaryType: 'count'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), []);
  });
  
  QUnit.test("Several total summary items in different column", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'name',
                  summaryType: 'count'
              },
              {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[{
              value: 2,
              column: 'name',
              summaryType: 'count'
          }], [{
              value: 25,
              column: 'age',
              summaryType: 'max'
          }]
          ]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("total summary item with incorrect column", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'incorrectName',
                  summaryType: 'count'
              },
              {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              value: 25,
              column: 'age',
              summaryType: 'max'
          }]]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("total custom summary when calculateCustomSummary not implemented", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Alex', age: 23 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'name',
                  summaryType: 'count'
              }, {
                  showInColumn: 'age',
                  summaryType: 'custom'
              }]
          }
      };
  
      var errorId;
  
      errors.log = function(id) {
          if(!errorId) {
              errorId = id;
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.equal(errorId, 'E1026', 'error message');
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("total custom summary", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Alex', age: 23 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'name',
                  summaryType: 'count'
              }, {
                  name: 'AlexAvgAge',
                  showInColumn: 'age',
                  summaryType: 'custom'
              }],
              calculateCustomSummary: function(options) {
                  if(options.name === 'AlexAvgAge') {
                      if(options.summaryProcess === 'start') {
                          options.totalValue = [0, 0];
                      }
                      if(options.summaryProcess === 'calculate') {
                          if(options.value.name === 'Alex') {
                              options.totalValue = [options.totalValue[0] + options.value.age, options.totalValue[1] + 1];
                          }
                      }
                      if(options.summaryProcess === 'finalize') {
                          options.totalValue = options.totalValue[0] / options.totalValue[1];
                      }
                  }
              }
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[{
              value: 3,
              column: 'name',
              summaryType: 'count'
          }], [{
              name: 'AlexAvgAge',
              value: 21,
              showInColumn: 'age',
              summaryType: 'custom'
          }]]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("total custom summary by selection", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Alex', age: 23 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  name: 'SelectedAvgAge',
                  showInColumn: 'age',
                  summaryType: 'custom'
              }],
              calculateCustomSummary: function(options) {
                  if(options.name === 'SelectedAvgAge') {
                      if(options.summaryProcess === 'start') {
                          options.totalValue = [0, 0];
                      }
                      if(options.summaryProcess === 'calculate') {
                          if(options.component.isRowSelected(options.value)) {
                              options.totalValue = [options.totalValue[0] + options.value.age, options.totalValue[1] + 1];
                          }
                      }
                      if(options.summaryProcess === 'finalize') {
                          options.totalValue = options.totalValue[0] / options.totalValue[1];
                      }
                  }
              }
          },
          selectedRowKeys: [{ name: 'Alex', age: 23 }, { name: 'Dan', age: 25 }]
      };
  
      //act
      setupDataGridModules(this, ['data', 'columns', 'selection', 'summary']);
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[], [{
              name: 'SelectedAvgAge',
              value: 24,
              showInColumn: 'age',
              summaryType: 'custom'
          }]]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("Several total summary items in different column", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'name',
                  summaryType: 'count'
              },
              {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[{
              value: 2,
              column: 'name',
              summaryType: 'count'
          }], [{
              value: 25,
              column: 'age',
              summaryType: 'max'
          }]
          ]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("Changing total summary items", function(assert) {
      this.options = {
          dataSource: [
              { name: 'Alex', age: 19 },
              { name: 'Dan', age: 25 }
          ],
          summary: {
              totalItems: [{
                  column: 'name',
                  summaryType: 'count'
              }]
          }
      };
  
      this.setupDataGridModules();
      this.clock.tick();
  
      //act
      this.options.summary.totalItems.push({
          column: 'age',
          summaryType: 'max'
      });
  
      this.dataController.optionChanged({ name: 'summary' });
      this.clock.tick();
  
  
      //assert
      assert.deepEqual(this.dataController.footerItems(), [{
          rowType: 'totalFooter', summaryCells: [[{
              value: 2,
              column: 'name',
              summaryType: 'count'
          }], [{
              value: 25,
              column: 'age',
              summaryType: 'max'
          }]
          ]
      }]);
      assert.ok(!this.dataController.isLoading());
  });
  
  QUnit.test("group summary item in group cell", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          summary: {
              groupItems: [{
                  column: 'age',
                  summaryType: 'min'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[{
          value: 15,
          column: 'age',
          columnCaption: 'Age',
          summaryType: 'min'
      }], []]);
      assert.deepEqual(this.dataController.items()[1].summaryCells, [[{
          value: 25,
          columnCaption: 'Age',
          column: 'age',
          summaryType: 'min'
      }], []]);
  });
  
  QUnit.test("Group footer is hidden when group summary is defined a for one a grouped column", function(assert) {
      this.options = {
          columns: [{ dataField: 'name', groupIndex: 0 }, 'age'],
          grouping: {
              autoExpandAll: true
          },
          dataSource: {
              group: "name",
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          summary: {
              groupItems: [{
                  column: 'name',
                  summaryType: 'count',
                  showInGroupFooter: true
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(this.dataController.items().length, 5);
      assert.equal(this.dataController.items()[0].rowType, "group");
      assert.equal(this.dataController.items()[1].rowType, "data");
      assert.equal(this.dataController.items()[2].rowType, "data");
      assert.equal(this.dataController.items()[3].rowType, "group");
      assert.equal(this.dataController.items()[4].rowType, "data");
  });
  
  QUnit.test("Show summary value in group footer when showWhenGrouped is true for grouped column", function(assert) {
      this.options = {
          columns: [{ dataField: 'name', groupIndex: 0, showWhenGrouped: true }, 'age'],
          grouping: {
              autoExpandAll: true
          },
          dataSource: {
              group: "name",
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          summary: {
              groupItems: [{
                  column: 'name',
                  summaryType: 'count',
                  showInGroupFooter: true
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(this.dataController.items().length, 7);
      assert.equal(this.dataController.items()[0].rowType, "group");
      assert.equal(this.dataController.items()[1].rowType, "data");
      assert.equal(this.dataController.items()[2].rowType, "data");
      assert.equal(this.dataController.items()[3].rowType, "groupFooter");
      assert.equal(this.dataController.items()[4].rowType, "group");
      assert.equal(this.dataController.items()[5].rowType, "data");
      assert.equal(this.dataController.items()[6].rowType, "groupFooter");
  });
  
  QUnit.test("group sorting by summary", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 },
                  { name: 'Sam', age: 20 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 }
              ]
          },
          sortByGroupSummaryInfo: [{
              summaryItem: 'count'
          }],
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data.key, 'Dan');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 1);
      assert.deepEqual(this.dataController.items()[1].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[2].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[2].summaryCells[0][0].value, 3);
  });
  
  QUnit.test("group sorting by summary when change grouping", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 },
                  { name: 'Sam', age: 20 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 }
              ]
          },
          sortByGroupSummaryInfo: [{
              summaryItem: 'count',
              groupColumn: 'name'
          }],
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }]
          }
      };
  
      this.setupDataGridModules();
      this.clock.tick();
  
      //act
      this.columnsController.columnOption('name', 'groupIndex', 0);
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data.key, 'Dan');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 1);
      assert.deepEqual(this.dataController.items()[1].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[2].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[2].summaryCells[0][0].value, 3);
  });
  
  QUnit.test("Changing sortByGroupSummaryInfo", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 },
                  { name: 'Sam', age: 20 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 }
              ]
          },
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }]
          }
      };
  
      this.setupDataGridModules();
      this.clock.tick();
  
      //act
      this.options.sortByGroupSummaryInfo = [{
          summaryItem: 'count'
      }];
  
      this.dataController.optionChanged({ name: 'sortByGroupSummaryInfo' });
      this.clock.tick();
  
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data.key, 'Dan');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 1);
      assert.deepEqual(this.dataController.items()[1].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[2].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[2].summaryCells[0][0].value, 3);
  });
  
  QUnit.test("group sorting by summary when several columns grouped", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: ['name', 'age'],
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 },
                  { name: 'Sam', age: 20 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: true
          },
          sortByGroupSummaryInfo: [{
              groupColumn: 'name',
              summaryItem: 'count',
              sortOrder: 'desc'
          }, {
              groupColumn: 'age',
              summaryItem: 'max',
              sortOrder: 'desc'
          }],
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }, {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.deepEqual(this.dataController.items()[0].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 3);
      assert.deepEqual(this.dataController.items()[1].data.key, 20);
      assert.deepEqual(this.dataController.items()[1].summaryCells[1][1].value, 20);
  });
  
  QUnit.test("group sorting by first summary", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 },
                  { name: 'Sam', age: 20 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 }
              ]
          },
          sortByGroupSummaryInfo: [{
              summaryItem: 0
          }],
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data.key, 'Dan');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 1);
      assert.deepEqual(this.dataController.items()[1].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[2].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[2].summaryCells[0][0].value, 3);
  });
  
  QUnit.test("findSummaryItem by custom name", function(assert) {
      var summaryItems = [{
          name: 'testCount1',
          summaryType: 'count'
      }, {
          name: 'testCount2',
          summaryType: 'count'
      }, {
          name: 'testMin',
          summaryType: 'min',
          column: 'testField'
      }];
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.strictEqual(this.dataController._findSummaryItem(summaryItems, 'count'), 0, 'find by summaryType');
      assert.strictEqual(this.dataController._findSummaryItem(summaryItems, 'testField'), 2, 'find by column name');
      assert.strictEqual(this.dataController._findSummaryItem(summaryItems, 'min_testField'), 2, 'find by summary type+column name');
      assert.strictEqual(this.dataController._findSummaryItem(summaryItems, 'testCount2'), 1, 'find by name');
      assert.strictEqual(this.dataController._findSummaryItem(summaryItems, 1), 1, 'find by summary item index');
      assert.strictEqual(this.dataController._findSummaryItem(summaryItems, 'test3'), -1, 'find by wrong name');
  });
  
  QUnit.test("group sorting by summary sortOrder desc", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 },
                  { name: 'Sam', age: 20 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 }
              ]
          },
          sortByGroupSummaryInfo: [{
              summaryItem: 'count',
              sortOrder: 'desc'
          }],
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 3);
      assert.deepEqual(this.dataController.items()[1].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[2].data.key, 'Dan');
      assert.deepEqual(this.dataController.items()[2].summaryCells[0][0].value, 1);
  });
  
  QUnit.test("group sorting by several summaries", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 }
              ]
          },
          sortByGroupSummaryInfo: [{
              summaryItem: 'count',
              sortOrder: 'desc'
          }, {
              summaryItem: 'max',
              sortOrder: 'asc'
          }],
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }, {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][1].value, 19);
      assert.deepEqual(this.dataController.items()[1].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][1].value, 25);
      assert.deepEqual(this.dataController.items()[2].data.key, 'Dan');
      assert.deepEqual(this.dataController.items()[2].summaryCells[0][0].value, 1);
  });
  
  QUnit.test("group sorting groupColumn fo grouped column only", function(assert) {
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Dan', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Alex', age: 25 },
                  { name: 'Sam', age: 18 },
                  { name: 'Sam', age: 19 }
              ]
          },
          sortByGroupSummaryInfo: [{
              groupColumn: 'name',
              summaryItem: 'count',
              sortOrder: 'desc'
          }, {
              groupColumn: 'age',
              summaryItem: 'max',
              sortOrder: 'asc'
          }],
          summary: {
              groupItems: [{
                  summaryType: 'count'
              }, {
                  column: 'age',
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 3);
      assert.deepEqual(this.dataController.items()[0].data.key, 'Alex');
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[0].summaryCells[0][1].value, 25);
      assert.deepEqual(this.dataController.items()[1].data.key, 'Sam');
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][0].value, 2);
      assert.deepEqual(this.dataController.items()[1].summaryCells[0][1].value, 19);
      assert.deepEqual(this.dataController.items()[2].data.key, 'Dan');
      assert.deepEqual(this.dataController.items()[2].summaryCells[0][0].value, 1);
  });
  
  QUnit.test("group custom summary item", function(assert) {
      var startCount = 0,
          calculateCount = 0,
          finalizeCount = 0;
  
      var totalValue;
      this.options = {
          columns: ['name', 'age'],
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 20 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          summary: {
              groupItems: [{
                  name: 'CountFromAge18',
                  summaryType: 'custom'
              }],
              calculateCustomSummary: function(options) {
                  if(options.name === 'CountFromAge18') {
                      if(options.summaryProcess === 'start') {
                          startCount++;
                          totalValue = 0;
                      }
                      if(options.summaryProcess === 'calculate') {
                          calculateCount++;
                          if(options.value.age >= 18) {
                              totalValue++;
                          }
                      }
                      if(options.summaryProcess === 'finalize') {
                          finalizeCount++;
                          options.totalValue = totalValue;
                      }
                  }
              }
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[{
          value: 2,
          name: 'CountFromAge18',
          summaryType: 'custom'
      }], []]);
      assert.deepEqual(this.dataController.items()[1].summaryCells, [[{
          value: 1,
          name: 'CountFromAge18',
          summaryType: 'custom'
      }], []]);
  
      //T278115
      assert.strictEqual(startCount, 2, "start count");
      assert.strictEqual(calculateCount, 4, "calculate count");
      assert.strictEqual(finalizeCount, 2, "finalize count");
  });
  
  QUnit.test("group summary item alignByColumn", function(assert) {
      this.options = {
          dataSource: {
              group: 'name',
              store: [
                  { id: 1, name: 'Alex', age: 19 },
                  { id: 2, name: 'Alex', age: 15 },
                  { id: 3, name: 'Dan', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: true
          },
          summary: {
              groupItems: [{
                  column: 'age',
                  alignByColumn: true,
                  summaryType: 'min'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items()[0].rowType, 'group');
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[], [], [
          $.extend({ value: 15 }, this.options.summary.groupItems[0])
      ]]);
      assert.strictEqual(this.dataController.items()[3].rowType, 'group');
      assert.deepEqual(this.dataController.items()[3].summaryCells, [[], [], [
          $.extend({ value: 25 }, this.options.summary.groupItems[0])
      ]]);
  });
  
  QUnit.test("group summary items with and without alignByColumn", function(assert) {
      this.options = {
          dataSource: {
              group: 'name',
              store: [
                  { id: 0, name: 'Alex', age: 19 },
                  { id: 1, name: 'Alex', age: 15 },
                  { id: 2, name: 'Dan', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: true
          },
          summary: {
              groupItems: [{
                  column: 'name',
                  summaryType: 'count'
              }, {
                  column: 'age',
                  alignByColumn: true,
                  summaryType: 'min'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items()[0].rowType, 'group');
      assert.deepEqual(this.dataController.items()[0].summaryCells, [
          [$.extend({ value: 2 }, this.options.summary.groupItems[0])], [],
          [$.extend({ value: 15 }, this.options.summary.groupItems[1])]
      ]);
      assert.strictEqual(this.dataController.items()[3].rowType, 'group');
      assert.deepEqual(this.dataController.items()[3].summaryCells, [
          [$.extend({ value: 1 }, this.options.summary.groupItems[0])], [],
          [$.extend({ value: 25 }, this.options.summary.groupItems[1])]
      ]);
  });
  
  //T241954
  QUnit.test("group summary item alignByColumn for first cell after group", function(assert) {
      this.options = {
          dataSource: {
              group: ['city', 'name'],
              store: [
                  { city: 'London', name: 'Alex', age: 19 },
                  { city: 'London', name: 'Alex', age: 15 },
                  { city: 'London', name: 'Dan', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: true
          },
          summary: {
              groupItems: [{
                  column: 'age',
                  alignByColumn: true,
                  summaryType: 'sum'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items()[0].rowType, 'group');
      assert.deepEqual(this.dataController.items()[0].summaryCells, [[
          $.extend({ value: 59, columnCaption: "Age" }, this.options.summary.groupItems[0])
      ], [], []]);
      assert.strictEqual(this.dataController.items()[1].rowType, 'group');
      assert.deepEqual(this.dataController.items()[1].summaryCells, [[], [
          $.extend({ value: 34, columnCaption: "Age" }, this.options.summary.groupItems[0])
      ], []]);
      assert.strictEqual(this.dataController.items()[4].rowType, 'group');
      assert.deepEqual(this.dataController.items()[4].summaryCells, [[], [
          $.extend({ value: 25, columnCaption: "Age" }, this.options.summary.groupItems[0])
      ], []]);
  });
  
  QUnit.test("group summary item with showInGroupFooter when no autoExpandAll", function(assert) {
      this.options = {
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          summary: {
              groupItems: [{
                  showInGroupFooter: true,
                  column: 'age',
                  summaryType: 'min'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 2);
      assert.deepEqual(this.dataController.items()[0].summaryCells, []);
      assert.deepEqual(this.dataController.items()[1].summaryCells, []);
  });
  
  QUnit.test("No groupFooter item when group summary items with showInGroupFooter and no autoExpandAll", function(assert) {
      this.options = {
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: false
          },
          summary: {
              groupItems: [{
                  column: 'age',
                  showInGroupFooter: true,
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 2);
      assert.strictEqual(this.dataController.items()[0].rowType, 'group');
      assert.strictEqual(this.dataController.items()[1].rowType, 'group');
  });
  
  QUnit.test("groupFooter item when group summary items with showInGroupFooter and autoExpandAll", function(assert) {
      this.options = {
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: true
          },
          summary: {
              groupItems: [{
                  column: 'age',
                  showInGroupFooter: true,
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 7);
      assert.strictEqual(this.dataController.items()[0].rowType, 'group');
      assert.strictEqual(this.dataController.items()[1].rowType, 'data');
      assert.strictEqual(this.dataController.items()[2].rowType, 'data');
      assert.strictEqual(this.dataController.items()[3].rowType, 'groupFooter');
      assert.deepEqual(this.dataController.items()[3].summaryCells, [[],
          [$.extend({ value: 19 }, this.options.summary.groupItems[0])]
      ]);
      assert.strictEqual(this.dataController.items()[4].rowType, 'group');
      assert.strictEqual(this.dataController.items()[5].rowType, 'data');
      assert.strictEqual(this.dataController.items()[6].rowType, 'groupFooter');
      assert.deepEqual(this.dataController.items()[6].summaryCells, [[],
          [$.extend({ value: 25 }, this.options.summary.groupItems[0])]
      ]);
  });
  
  QUnit.test("several groupFooter items when group summary items with showInGroupFooter and autoExpandAll", function(assert) {
      this.options = {
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: true
          },
          summary: {
              groupItems: [{
                  column: 'age',
                  showInGroupFooter: true,
                  summaryType: 'min'
              },
              {
                  column: 'age',
                  showInGroupFooter: true,
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 7);
      assert.strictEqual(this.dataController.items()[0].rowType, 'group');
      assert.strictEqual(this.dataController.items()[1].rowType, 'data');
      assert.strictEqual(this.dataController.items()[2].rowType, 'data');
      assert.strictEqual(this.dataController.items()[3].rowType, 'groupFooter');
      assert.deepEqual(this.dataController.items()[3].summaryCells, [[], [
          $.extend({ value: 15 }, this.options.summary.groupItems[0]),
          $.extend({ value: 19 }, this.options.summary.groupItems[1])
      ]]);
      assert.strictEqual(this.dataController.items()[4].rowType, 'group');
      assert.strictEqual(this.dataController.items()[5].rowType, 'data');
      assert.strictEqual(this.dataController.items()[6].rowType, 'groupFooter');
  });
  
  QUnit.test("not show groupFooter item for grouped column", function(assert) {
      this.options = {
          dataSource: {
              group: 'name',
              store: [
                  { name: 'Alex', age: 19 },
                  { name: 'Alex', age: 15 },
                  { name: 'Dan', age: 25 }
              ]
          },
          grouping: {
              autoExpandAll: true
          },
          summary: {
              groupItems: [{
                  column: 'age',
                  showInColumn: 'name',
                  showInGroupFooter: true,
                  summaryType: 'min'
              },
              {
                  column: 'age',
                  showInGroupFooter: true,
                  summaryType: 'max'
              }]
          }
      };
  
      //act
      this.setupDataGridModules();
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataController.isLoading());
      assert.strictEqual(this.dataController.items().length, 7);
      assert.strictEqual(this.dataController.items()[0].rowType, 'group');
      assert.strictEqual(this.dataController.items()[1].rowType, 'data');
      assert.strictEqual(this.dataController.items()[2].rowType, 'data');
      assert.strictEqual(this.dataController.items()[3].rowType, 'groupFooter');
      assert.deepEqual(this.dataController.items()[3].summaryCells, [[], [
          $.extend({ value: 19 }, this.options.summary.groupItems[1])
      ]]);
      assert.strictEqual(this.dataController.items()[4].rowType, 'group');
      assert.strictEqual(this.dataController.items()[5].rowType, 'data');
      assert.strictEqual(this.dataController.items()[6].rowType, 'groupFooter');
  });
  
  QUnit.module("Master Detail", {
      beforeEach: function() {
          this.array = [];
          for(var i = 0; i < 10; i++) {
              this.array.push({ id: i, group: i % 2, name: 'test' + i });
          }
          this.options = {
              masterDetail: {
                  enabled: true,
                  autoExpandAll: false
              },
              columns: [{ dataField: 'id' }, { dataField: 'group' }, { dataField: 'name' }],
              dataSource: {
                  asyncLoadEnabled: false,
                  store: {
                      type: 'array',
                      data: this.array,
                      key: 'id'
                  }
              },
              paging: {
                  enabled: true,
                  pageSize: 5
              }
          };
  
          this.setupDataGrid = function() {
              setupDataGridModules(this, ['data', 'columns', 'grouping', 'masterDetail', 'selection']);
          };
  
          this.clock = sinon.useFakeTimers();
      }, afterEach: function() {
          this.dispose();
          this.clock.restore();
      }
  });
  
  QUnit.test("items by default", function(assert) {
      //act
      this.setupDataGrid();
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 5);
  
      for(var i = 0; i < items.length; i++) {
          assert.strictEqual(items[i].rowType, 'data');
          assert.strictEqual(items[i].isExpanded, false);
          assert.strictEqual(items[i].values[0], false);
      }
  });
  
  QUnit.test("items after expand one row", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(1);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[0].isExpanded, false);
      assert.strictEqual(items[0].values[0], false);
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[1].isExpanded, true);
      assert.strictEqual(items[1].values[0], true);
      assert.strictEqual(items[2].rowType, 'detail');
      assert.strictEqual(items[2].key, 1);
      assert.strictEqual(items[2].data.name, 'test1');
  });
  
  QUnit.test("expand events after expand/collapse one row", function(assert) {
      var events = [];
      var that = this;
      $.each(["onRowExpanding", "onRowExpanded", "onRowCollapsing", "onRowCollapsed"], function(index, name) {
          that.options[name] = function(e) {
              events.push({ name: name, key: e.key });
          };
      });
  
      this.setupDataGrid();
  
      //act
      this.dataController.changeRowExpand(1);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[1].isExpanded, true);
      assert.strictEqual(items[2].rowType, 'detail');
      assert.strictEqual(items[2].key, 1);
      assert.strictEqual(events.length, 2);
  
      //act
      this.dataController.changeRowExpand(1);
  
      //assert
      items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[1].isExpanded, false);
      assert.strictEqual(events.length, 4);
  
      assert.deepEqual(events, [
          { name: "onRowExpanding", key: 1 },
          { name: "onRowExpanded", key: 1 },
          { name: "onRowCollapsing", key: 1 },
          { name: "onRowCollapsed", key: 1 }
      ], "expand events");
  });
  
  QUnit.test("items when autoExpandAll", function(assert) {
      //act
      this.options.masterDetail.autoExpandAll = true;
      this.setupDataGrid();
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 10);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'detail');
      assert.strictEqual(items[2].rowType, 'data');
      assert.strictEqual(items[3].rowType, 'detail');
  });
  
  
  QUnit.test("items after expandAll", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.expandAll(-1);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 10);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[0].isExpanded, true);
      assert.strictEqual(items[0].values[0], true);
      assert.strictEqual(items[1].rowType, 'detail');
      assert.strictEqual(items[2].values[0], true);
      assert.strictEqual(items[2].rowType, 'data');
      assert.strictEqual(items[2].isExpanded, true);
      assert.strictEqual(items[3].rowType, 'detail');
  });
  
  QUnit.test("items after collapseAll", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(1);
      this.dataController.collapseAll(-1);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 5);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[2].rowType, 'data');
      assert.strictEqual(items[3].rowType, 'data');
      assert.strictEqual(items[4].rowType, 'data');
  });
  
  QUnit.test("isRowExpanded", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(1);
  
      //assert
      assert.strictEqual(this.dataController.isRowExpanded(0), false);
      assert.strictEqual(this.dataController.isRowExpanded(1), true);
      assert.strictEqual(this.dataController.isRowExpanded(2), false);
  });
  
  QUnit.test("isRowExpanded after expandAll", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.expandAll(-1);
      this.dataController.changeRowExpand(1);
  
      //assert
      assert.strictEqual(this.dataController.isRowExpanded(0), true);
      assert.strictEqual(this.dataController.isRowExpanded(1), false);
      assert.strictEqual(this.dataController.isRowExpanded(2), true);
  });
  
  
  QUnit.test("expandRow by rowIndex", function(assert) {
      this.setupDataGrid();
  
      //act
      this.dataController.expandRow(this.getKeyByRowIndex(0));
      this.dataController.expandRow(this.getKeyByRowIndex(2));
  
  
      //assert
      assert.strictEqual(this.dataController.isRowExpanded(0), true);
      assert.strictEqual(this.dataController.isRowExpanded(1), true);
      assert.strictEqual(this.dataController.isRowExpanded(2), false);
  });
  
  QUnit.test("expandRow", function(assert) {
      this.setupDataGrid();
  
      this.dataController.changeRowExpand(1);
  
      //act
      this.dataController.expandRow(0);
      this.dataController.expandRow(1);
  
  
      //assert
      assert.strictEqual(this.dataController.isRowExpanded(0), true);
      assert.strictEqual(this.dataController.isRowExpanded(1), true);
      assert.strictEqual(this.dataController.isRowExpanded(2), false);
  });
  
  QUnit.test("collapseRow", function(assert) {
      this.setupDataGrid();
  
      this.dataController.changeRowExpand(1);
      this.dataController.changeRowExpand(2);
  
      //act
      var collapseRowResult0 = this.dataController.collapseRow(0);
      var collapseRowResult1 = this.dataController.collapseRow(1);
  
      //assert
      assert.ok(collapseRowResult0 && collapseRowResult0.done, "collapseRow result 0 is deferred");
      assert.ok(collapseRowResult0.state(), "resolved", "collapseRow result 0 state is resolved");
      assert.ok(collapseRowResult1 && collapseRowResult1.done, "collapseRow result 1 is deferred");
      assert.ok(collapseRowResult1.state(), "resolved", "collapseRow result state 1 is resolved");
      assert.strictEqual(this.dataController.isRowExpanded(0), false);
      assert.strictEqual(this.dataController.isRowExpanded(1), false);
      assert.strictEqual(this.dataController.isRowExpanded(2), true);
  });
  
  QUnit.test("items after expand several rows", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(0);
      this.dataController.changeRowExpand(2);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 7);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'detail');
      assert.strictEqual(items[1].key, 0);
      assert.strictEqual(items[1].data.name, 'test0');
      assert.strictEqual(items[2].rowType, 'data');
      assert.strictEqual(items[3].rowType, 'data');
      assert.strictEqual(items[4].rowType, 'detail');
      assert.strictEqual(items[4].key, 2);
      assert.strictEqual(items[4].data.name, 'test2');
  });
  
  QUnit.test("expand events after expand several data rows", function(assert) {
      var events = [];
      var that = this;
      $.each(["onRowExpanding", "onRowExpanded", "onRowCollapsing", "onRowCollapsed"], function(index, name) {
          that.options[name] = function(e) {
              events.push({ name: name, key: e.key });
          };
      });
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(0);
      this.dataController.changeRowExpand(2);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 7);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'detail');
      assert.strictEqual(items[1].key, 0);
      assert.strictEqual(items[1].data.name, 'test0');
      assert.strictEqual(items[2].rowType, 'data');
      assert.strictEqual(items[3].rowType, 'data');
      assert.strictEqual(items[4].rowType, 'detail');
      assert.strictEqual(items[4].key, 2);
      assert.strictEqual(items[4].data.name, 'test2');
  
      assert.deepEqual(events, [
          { name: "onRowExpanding", key: 0 },
          { name: "onRowExpanded", key: 0 },
          { name: "onRowExpanding", key: 2 },
          { name: "onRowExpanded", key: 2 }
      ], "expand events");
  
  });
  
  QUnit.test("cancel expanding events", function(assert) {
      var events = [];
      var that = this;
      $.each(["onRowExpanding", "onRowExpanded", "onRowCollapsing", "onRowCollapsed"], function(index, name) {
          that.options[name] = function(e) {
              if(e.key === 0) {
                  e.cancel = true;
              }
              events.push({ name: name, key: e.key });
          };
      });
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(0);
      this.dataController.changeRowExpand(2);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[2].rowType, 'data');
      assert.strictEqual(items[3].rowType, 'detail');
      assert.strictEqual(items[3].key, 2);
      assert.strictEqual(items[3].data.name, 'test2');
  
      assert.deepEqual(events, [
          { name: "onRowExpanding", key: 0 },
          { name: "onRowExpanding", key: 2 },
          { name: "onRowExpanded", key: 2 }
      ], "expand events");
  
  });
  
  QUnit.test("expand events after expand group row", function(assert) {
      var events = [];
      var that = this;
      $.each(["onRowExpanding", "onRowExpanded", "onRowCollapsing", "onRowCollapsed"], function(index, name) {
          that.options[name] = function(e) {
              events.push({ name: name, key: e.key });
          };
      });
      this.options.columns[1].groupIndex = 0;
      this.options.loadingTimeout = 0;
      this.setupDataGrid();
  
      this.clock.tick();
  
      //act
      this.dataController.changeRowExpand([0]);
  
      //assert
      assert.equal(events.length, 1, "one expand event called");
  
      //act
      this.clock.tick();
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 5);
      assert.strictEqual(items[0].rowType, 'group');
      assert.strictEqual(items[0].data.key, 0);
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[2].rowType, 'data');
  
      assert.deepEqual(events, [
          { name: "onRowExpanding", key: [0] },
          { name: "onRowExpanded", key: [0] }
      ], "expand events");
  });
  
  QUnit.test("items after expand and change page", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(0);
      this.dataController.pageIndex(1);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 5);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[0].key, 5);
  
      //act
      this.dataController.pageIndex(0);
      items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[0].key, 0);
      assert.strictEqual(items[1].rowType, 'detail');
      assert.strictEqual(items[1].key, 0);
  });
  
  //T317450
  QUnit.test("items after expand and refresh", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(0);
      this.dataController.refresh();
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[0].key, 0);
      assert.strictEqual(items[1].rowType, 'detail');
      assert.strictEqual(items[1].key, 0);
  });
  
  QUnit.test("items after collapse expanded row", function(assert) {
      //arrange
      this.setupDataGrid();
      this.dataController.changeRowExpand(0);
      this.dataController.changeRowExpand(2);
  
      //act
      this.dataController.changeRowExpand(0);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 7, "count items");
      assert.strictEqual(items[0].rowType, "data", "rowType first item");
      assert.strictEqual(items[1].rowType, "detail", "rowType second item");
      assert.strictEqual(items[1].key, 0, "key second item");
      assert.ok(!items[1].visible, "visible second item");
      assert.strictEqual(items[1].data.name, "test0", "data name second item");
      assert.strictEqual(items[2].rowType, "data", "rowType third item");
      assert.strictEqual(items[3].rowType, "data", "rowType fourth item");
      assert.strictEqual(items[4].rowType, "detail", "rowType fifth item");
      assert.strictEqual(items[4].key, 2, "key fifth item");
      assert.ok(items[4].visible, "visible fifth item");
      assert.strictEqual(items[4].data.name, "test2", "data name fifth item");
  });
  
  QUnit.test("items after collapse expanded second row", function(assert) {
      //arrange
      this.setupDataGrid();
      this.dataController.changeRowExpand(1);
      this.dataController.changeRowExpand(2);
  
      //act
      this.dataController.changeRowExpand(1);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 7, "count items");
      assert.strictEqual(items[0].rowType, "data", "rowType first item");
      assert.strictEqual(items[1].rowType, "data", "rowType second item");
      assert.strictEqual(items[2].rowType, "detail", "rowType third item");
      assert.strictEqual(items[2].key, 1, "key third item");
      assert.ok(!items[2].visible, "visible third item");
      assert.strictEqual(items[2].data.name, "test1", "data name third item");
      assert.strictEqual(items[3].rowType, "data", "rowType fourth item");
      assert.strictEqual(items[4].rowType, "detail", "rowType fifth item");
      assert.strictEqual(items[4].key, 2, "key fifth item");
      assert.ok(items[4].visible, "visible fifth item");
      assert.strictEqual(items[4].data.name, "test2", "data name fifth item");
  });
  
  QUnit.test("expand row on invisible page", function(assert) {
      this.setupDataGrid();
      this.dataController.changeRowExpand([0]);
  
      //act
      this.dataController.changeRowExpand(6);
      this.dataController.pageIndex(1);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[2].rowType, 'detail');
      assert.strictEqual(items[2].key, 6);
      assert.strictEqual(items[2].data.name, 'test6');
  });
  
  QUnit.test("items after expand data row when grouped column exists", function(assert) {
      this.options.columns[1].groupIndex = 0;
      this.setupDataGrid();
      this.dataController.changeRowExpand([0]);
  
      //act
      this.dataController.changeRowExpand(0);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 6);
      assert.strictEqual(items[0].rowType, 'group');
      assert.strictEqual(items[0].data.key, 0);
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[1].isExpanded, true);
      assert.deepEqual(items[1].values, [null, true, 0, "test0"]);
      assert.strictEqual(items[2].rowType, 'detail');
      assert.strictEqual(items[2].key, 0);
      assert.strictEqual(items[3].rowType, 'data');
      assert.strictEqual(items[3].key, 2);
      assert.strictEqual(items[3].isExpanded, false);
      assert.deepEqual(items[3].values, [null, false, 2, "test2"]);
      assert.strictEqual(items[4].rowType, 'data');
      assert.strictEqual(items[5].rowType, 'data');
  });
  
  QUnit.test("items after expand data row and collapse its group", function(assert) {
      this.options.columns[1].groupIndex = 0;
      this.setupDataGrid();
  
      this.dataController.changeRowExpand([0]);
  
      //act
      this.dataController.changeRowExpand(0);
      this.dataController.changeRowExpand([0]);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 2);
      assert.strictEqual(items[0].rowType, 'group');
      assert.strictEqual(items[1].rowType, 'group');
  });
  
  QUnit.test("detail items after disable masterDetail", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(1);
  
      this.options.masterDetail.enabled = false;
      this.dataController.optionChanged({ name: 'masterDetail', fullName: 'masterDetail.enabled' });
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 5);
      assert.strictEqual(items[0].rowType, 'data');
      assert.strictEqual(items[1].rowType, 'data');
      assert.strictEqual(items[2].rowType, 'data');
      assert.strictEqual(items[3].rowType, 'data');
      assert.strictEqual(items[4].rowType, 'data');
  });
  
  //T175307
  QUnit.test("Not reset master detail items after reassign same masterDetail options", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(1);
  
      var oldMasterDetail = this.options.masterDetail;
  
      this.options.masterDetail = { enabled: true, autoExpandAll: false };
  
      this.dataController.optionChanged({ name: 'masterDetail', fullName: 'masterDetail', previousValue: oldMasterDetail, value: this.options.masterDetail });
  
      //assert
      assert.ok(this.dataController.isRowExpanded(1), 'row 1 is expanded');
      assert.ok(!this.dataController.isRowExpanded(2), 'row 2 is not expanded');
  });
  
  QUnit.test("Reset master detail items after change masterDetail options", function(assert) {
      this.setupDataGrid();
      //act
      this.dataController.changeRowExpand(1);
  
      var oldMasterDetail = this.options.masterDetail;
  
      this.options.masterDetail = { enabled: true, autoExpandAll: true };
  
      this.dataController.optionChanged({ name: 'masterDetail', fullName: 'masterDetail', previousValue: oldMasterDetail, value: this.options.masterDetail });
  
      //assert
      assert.ok(this.dataController.isRowExpanded(1), 'row 1 is not expanded');
      assert.ok(this.dataController.isRowExpanded(2), 'row 2 is not expanded');
  });
  
  //T199951
  QUnit.test("Visible item after expanded/collapsed", function(assert) {
      //arrange
      this.setupDataGrid();
  
      //act
      this.dataController.changeRowExpand(0);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items[0].rowType, "data");
      assert.strictEqual(items[0].key, 0);
      assert.strictEqual(items[1].rowType, "detail");
      assert.strictEqual(items[1].key, 0);
      assert.ok(items[1].visible, "visible master detail");
  
      //act
      this.dataController.changeRowExpand(0);
  
      //assert
      items = this.dataController.items();
      assert.strictEqual(items[0].rowType, "data");
      assert.strictEqual(items[0].key, 0);
      assert.strictEqual(items[1].rowType, "detail");
      assert.strictEqual(items[1].key, 0);
      assert.ok(!items[1].visible, "visible master detail");
  
      var callChanged = false;
  
      this.dataController.changed.add(function(change) {
          callChanged = true;
  
          //assert
          assert.ok(change.items[1].visible, "visible master detail");
          assert.ok(!change.items[1].hasOwnProperty("rowType"), "not have property rowType");
          assert.ok(!change.items[1].hasOwnProperty("key"), "not have property key");
      });
  
      //act
      this.dataController.changeRowExpand(0);
  
      assert.ok(callChanged, "call changed");
  });
  
  QUnit.test("Remove invisible expanded items after change pageIndex", function(assert) {
      //arrange
      this.setupDataGrid();
  
      //act
      this.dataController.changeRowExpand(0);
      this.dataController.changeRowExpand(0);
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items[0].rowType, "data");
      assert.strictEqual(items[0].key, 0);
      assert.strictEqual(items[1].rowType, "detail");
      assert.strictEqual(items[1].key, 0);
      assert.strictEqual(items[1].visible, false, "visible master detail");
  
      //act
      this.dataController.pageIndex(1);
      this.dataController.pageIndex(0);
  
      //assert
      items = this.dataController.items();
      assert.strictEqual(items[0].rowType, "data");
      assert.strictEqual(items[0].key, 0);
      assert.strictEqual(items[1].rowType, "data");
      assert.strictEqual(items[1].key, 1);
      assert.strictEqual(items[1].visible, undefined, "second data row is visible");
  });
  
  QUnit.test("SelectAll after expand and collapse row", function(assert) {
      //arrange
      this.setupDataGrid();
      this.dataController.changeRowExpand(0);
      this.dataController.changeRowExpand(0);
  
      //act
      this.selectAll();
  
      //assert
      var items = this.dataController.items();
      assert.strictEqual(items.length, 6, "item count");
      assert.strictEqual(items[0].rowType, "data");
      assert.ok(items[0].isSelected, "first data row is selected");
      assert.strictEqual(items[0].key, 0);
      assert.strictEqual(items[1].rowType, "detail");
      assert.strictEqual(items[1].key, 0);
      assert.strictEqual(items[1].visible, false, "visible master detail");
      assert.ok(!items[1].isSelected, "detail row is not selected");
      assert.ok(items[items.length - 1].isSelected, "last row is selected");
  });
  
  QUnit.module("Partial update", {
      beforeEach: function() {
          var that = this;
          that.setupModules = function() {
              setupModule.call(that);
  
              that.array = [
                  { name: 'Alex', age: 30 },
                  { name: 'Dan', age: 25 },
                  { name: 'Bob', age: 20 }
              ];
              that.dataSource = createDataSource(that.array, { key: 'name' });
              that.dataController.setDataSource(that.dataSource);
              that.dataSource.load();
          };
  
      }, afterEach: teardownModule
  });
  
  QUnit.test("update one row", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems[0].age = 31;
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [0]
      });
  
      //assert
      dataSourceItems[0].age = 31;
      var items = this.dataController.items();
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 31]);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Alex', age: 31 });
      assert.notStrictEqual(oldItems[0], items[0]);
      assert.strictEqual(oldItems[1], items[1]);
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0]);
      assert.deepEqual(changedArgs.changeTypes, ['update']);
      assert.deepEqual(changedArgs.items, [items[0]]);
  });
  
  QUnit.test("update several rows", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems[0].age = 31;
      dataSourceItems[2].age = 26;
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [0, 2]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 31]);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Alex', age: 31 });
      assert.deepEqual(this.dataController.items()[2].values, ['Bob', 26]);
      assert.deepEqual(this.dataController.items()[2].data, { name: 'Bob', age: 26 });
  
      assert.notStrictEqual(oldItems[0], items[0]);
      assert.strictEqual(oldItems[1], items[1]);
      assert.notStrictEqual(oldItems[2], items[2]);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0, 2]);
      assert.deepEqual(changedArgs.changeTypes, ['update', 'update']);
      assert.deepEqual(changedArgs.items, [items[0], items[2]]);
  });
  
  QUnit.test("update with incorrect rowIndices", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems[0].age = 31;
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [0, -1, 0]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.deepEqual(this.dataController.items()[0].values, ['Alex', 31]);
      assert.deepEqual(this.dataController.items()[0].data, { name: 'Alex', age: 31 });
  
      assert.notStrictEqual(oldItems[0], items[0]);
      assert.strictEqual(oldItems[1], items[1]);
      assert.strictEqual(oldItems[2], items[2]);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0]);
      assert.deepEqual(changedArgs.changeTypes, ['update']);
      assert.deepEqual(changedArgs.items, [items[0]]);
  });
  
  //T266696
  QUnit.test("insert item with editing of the first item", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items(),
          changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems.unshift({ name: 'Mike', age: 40 });
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [0, 0]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items.length, 4);
      assert.deepEqual(items[0].values, ['Mike', 40]);
      assert.deepEqual(items[0].data, { name: 'Mike', age: 40 });
      assert.strictEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0, 1]);
      assert.deepEqual(changedArgs.changeTypes, ['insert', 'update']);
      assert.deepEqual(changedArgs.items, [items[0], items[1]]);
  });
  
  QUnit.test("insert one item", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems.unshift({ name: 'Mike', age: 40 });
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [0]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items.length, 4);
      assert.deepEqual(items[0].values, ['Mike', 40]);
      assert.deepEqual(items[0].data, { name: 'Mike', age: 40 });
      assert.deepEqual(items[1].values, ['Alex', 30]);
      assert.deepEqual(items[1].data, { name: 'Alex', age: 30 });
  
      assert.notStrictEqual(oldItems[0], items[0]);
      assert.strictEqual(oldItems[0], items[1]);
      assert.strictEqual(oldItems[1], items[2]);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0]);
      assert.deepEqual(changedArgs.changeTypes, ['insert']);
      assert.deepEqual(changedArgs.items, [items[0]]);
  });
  
  QUnit.test("update and insert", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems[0].age = 31;
      dataSourceItems.splice(1, 0, { name: 'Mike', age: 40 });
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [0, 1]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items.length, 4);
      assert.deepEqual(items[0].values, ['Alex', 31]);
      assert.deepEqual(items[0].data, { name: 'Alex', age: 31 });
      assert.deepEqual(items[1].values, ['Mike', 40]);
      assert.deepEqual(items[1].data, { name: 'Mike', age: 40 });
  
      assert.notStrictEqual(oldItems[0], items[0]);
      assert.notStrictEqual(oldItems[0], items[1]);
      assert.strictEqual(oldItems[1], items[2]);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0, 1]);
      assert.deepEqual(changedArgs.changeTypes, ['update', 'insert']);
      assert.deepEqual(changedArgs.items, [items[0], items[1]]);
  });
  
  QUnit.test("update and insert when edit mode is form_T318702", function(assert) {
      //arrange
      this.options = {
          editing: {
              mode: "form",
              allowUpdating: true,
              allowAdding: true
          }
      };
  
      this.setupModules();
  
      var changedArgs;
      this.dataController.changed.add(function(e) {
          changedArgs = e;
      });
  
      this.editingController.editRow(0);
      this.editingController.addRow();
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items[0].values[0], undefined, "value 1");
      assert.equal(items[0].values[1], undefined, "value 2");
      assert.equal(items[0].values[2], null, "value 3");
      assert.equal(items[0].rowType, "detail", "row type");
      assert.deepEqual(changedArgs.changeType, "update", "changed args changeType");
      assert.deepEqual(changedArgs.changeTypes, ["insert", "update"], "changed args changeType");
      assert.deepEqual(changedArgs.rowIndices, [0, 1], "changed args rowIndices");
  });
  
  QUnit.test("remove one item", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems.splice(1, 1);
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [1]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items.length, 2);
      assert.deepEqual(items[0].values, ['Alex', 30]);
      assert.deepEqual(items[0].data, { name: 'Alex', age: 30 });
      assert.deepEqual(items[1].values, ['Bob', 20]);
      assert.deepEqual(items[1].data, { name: 'Bob', age: 20 });
  
      assert.strictEqual(oldItems[0], items[0]);
      assert.notStrictEqual(oldItems[1], items[1]);
      assert.strictEqual(oldItems[2], items[1]);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [1]);
      assert.deepEqual(changedArgs.changeTypes, ['remove']);
  });
  
  QUnit.test("update and remove", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems[1].age = 26;
      dataSourceItems.splice(2, 1);
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [1, 2]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items.length, 2);
      assert.deepEqual(items[1].values, ['Dan', 26]);
      assert.deepEqual(items[1].data, { name: 'Dan', age: 26 });
  
      assert.strictEqual(oldItems[0], items[0]);
      assert.notStrictEqual(oldItems[1], items[1]);
      assert.notStrictEqual(oldItems[2], items[1]);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [1, 2]);
      assert.deepEqual(changedArgs.changeTypes, ['update', 'remove']);
      assert.deepEqual(changedArgs.items, [items[1]]);
  });
  
  //T186415
  QUnit.test("remove and update next row", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems[1].age = 26;
      dataSourceItems.splice(0, 1);
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [0, 1]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items.length, 2);
      assert.deepEqual(items[0].values, ['Dan', 26]);
      assert.deepEqual(items[1].values, ['Bob', 20]);
  
      assert.notStrictEqual(oldItems[0].data, items[0].data);
      assert.strictEqual(oldItems[1].data, items[0].data);
      assert.strictEqual(oldItems[2].data, items[1].data);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0, 0]);
      assert.deepEqual(changedArgs.changeTypes, ['remove', 'update']);
      assert.deepEqual(changedArgs.items, [items[0], items[0]]);
  });
  
  QUnit.test("insert and remove", function(assert) {
      this.setupModules();
      var dataSourceItems = this.dataController.dataSource().items();
  
      var oldItems = this.dataController.items().slice(0);
      var changedArgs;
  
      this.dataController.changed.add(function(args) {
          changedArgs = args;
      });
  
      //act
      dataSourceItems.splice(1, 1);
      dataSourceItems.unshift({ name: 'Mike', age: 40 });
      this.dataController.updateItems({
          changeType: 'update',
          rowIndices: [1, 0]
      });
  
      //assert
      var items = this.dataController.items();
  
      assert.equal(items.length, 3);
      assert.deepEqual(items[0].values, ['Mike', 40]);
      assert.deepEqual(items[1].values, ['Alex', 30]);
      assert.deepEqual(items[2].values, ['Bob', 20]);
  
      assert.notStrictEqual(oldItems[0], items[0]);
      assert.notStrictEqual(oldItems[1], items[1]);
      assert.strictEqual(oldItems[0], items[1]);
      assert.strictEqual(oldItems[2], items[2]);
  
      assert.deepEqual(changedArgs.changeType, 'update');
      assert.deepEqual(changedArgs.rowIndices, [0, 2]);
      assert.deepEqual(changedArgs.changeTypes, ['insert', 'remove']);
      assert.deepEqual(changedArgs.items[0], items[0]);
  });
  
  QUnit.module("Using DataSource instance", {
      beforeEach: function() {
          this.array = [
                  { field1: 1, field2: 2, field3: 3 },
                  { field1: 1, field2: 2, field3: 4 },
                  { field1: 2, field2: 2, field3: 5 },
                  { field1: 2, field2: 3, field3: 6 },
                  { field1: 2, field2: 3, field3: 7 }
          ];
  
          this.dataSource = new DataSource(this.array);
  
          this.setupDataGridModules = function(options) {
              this.options = options;
              setupDataGridModules(this, ['data', 'virtualScrolling', 'columns', 'filterRow', 'search', 'editing', 'grouping']);
          };
  
          this.applyOptions = function(options) {
              $.extend(this.options, options);
              this.columnsController.init();
          };
  
          this.clock = sinon.useFakeTimers();
      },
      afterEach: function() {
          this.dispose();
          this.clock.restore();
      }
  });
  
  
  QUnit.test("change filter", function(assert) {
      this.setupDataGridModules({
          dataSource: this.dataSource,
          searchPanel: {
              text: '3'
          }
      });
  
      this.clock.tick();
  
      //assert
      assert.ok(!this.dataSource.filter(), 'no filter');
      assert.equal(this.dataController.itemsCount(), 3);
      assert.equal(this.dataController.totalCount(), 3);
  
      //act
      this.dataSource.filter(['field1', '=', 2]);
      this.dataSource.load();
      this.clock.tick();
  
      //assert
      var filter = this.dataSource.filter();
      assert.deepEqual(this.dataSource.filter(), [filter[0], '=', 2], 'changed filter');
      assert.equal(this.dataController.items()[0].data.field3, 6);
      assert.equal(this.dataController.itemsCount(), 2);
      assert.equal(this.dataController.totalCount(), 2);
  });
  
  QUnit.test("change group", function(assert) {
      var changes = [];
  
      this.setupDataGridModules({
          dataSource: this.dataSource,
          grouping: {
              autoExpandAll: true
          }
      });
  
      this.clock.tick();
  
      this.dataController.changed.add(function() {
          changes.push('data');
      });
  
      this.columnsController.columnsChanged.add(function(e) {
          changes.push('columns');
      });
  
      //act
      this.dataSource.group('field1');
      this.dataSource.pageSize(5);
      this.dataSource.load();
      this.clock.tick();
  
      //assert
      assert.strictEqual(this.columnOption(0, 'groupIndex'), 0);
      assert.strictEqual(this.columnOption(1, 'groupIndex'), undefined);
      assert.strictEqual(this.columnOption(2, 'groupIndex'), undefined);
  
      assert.deepEqual(changes, ['columns', 'data']);
      assert.equal(this.dataController.itemsCount(), 5);
      assert.equal(this.dataController.totalItemsCount(), 8);
      assert.equal(this.dataController.items()[0].rowType, 'group');
      assert.equal(this.dataController.items()[1].rowType, 'data');
      assert.equal(this.dataController.items()[1].data.field3, 3);
      assert.equal(this.dataController.items()[2].rowType, 'data');
      assert.equal(this.dataController.items()[3].rowType, 'group');
      assert.equal(this.dataController.items()[4].rowType, 'data');
      assert.equal(this.dataController.items()[4].data.field3, 5);
  });
  
  QUnit.test("change sort", function(assert) {
      var changes = [];
  
      this.setupDataGridModules({
          dataSource: this.dataSource
      });
  
      this.clock.tick();
  
      this.dataController.changed.add(function() {
          changes.push('data');
      });
  
      this.columnsController.columnsChanged.add(function(e) {
          changes.push('columns');
      });
  
      //act
      this.dataSource.sort({ selector: 'field3', desc: true });
      this.dataSource.load();
      this.clock.tick();
  
      //assert
      assert.strictEqual(this.columnOption(0, 'sortIndex'), undefined);
      assert.strictEqual(this.columnOption(0, 'sortOrder'), undefined);
      assert.strictEqual(this.columnOption(1, 'sortIndex'), undefined);
      assert.strictEqual(this.columnOption(1, 'sortOrder'), undefined);
      assert.strictEqual(this.columnOption(2, 'sortIndex'), 0);
      assert.strictEqual(this.columnOption(2, 'sortOrder'), 'desc');
  
      assert.deepEqual(changes, ['columns', 'data']);
      assert.equal(this.dataController.itemsCount(), 5);
      assert.equal(this.dataController.totalItemsCount(), 5);
      assert.equal(this.dataController.items()[0].data.field3, 7);
      assert.equal(this.dataController.items()[4].data.field3, 3);
  });
  
  QUnit.test("change pageIndex", function(assert) {
      this.dataSource.pageSize(3);
  
      this.setupDataGridModules({
          dataSource: this.dataSource
      });
  
      this.clock.tick();
  
      //act
      this.dataSource.pageIndex(1);
      this.dataSource.load();
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.pageIndex(), 1);
      assert.equal(this.dataController.items()[0].data.field3, 6);
  });
  
  QUnit.test("reload reset pageIndex in infinite scrolling mode", function(assert) {
      this.dataSource.pageSize(3);
  
      this.setupDataGridModules({
          dataSource: this.dataSource,
          scrolling: { mode: 'infinite' }
      });
  
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.totalItemsCount(), 3);
      assert.equal(this.dataController.items().length, 3);
  
      //act
      this.pageIndex(1);
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.totalItemsCount(), 5);
      assert.equal(this.dataController.items().length, 5);
  
      //act
      this.dataSource.reload(true);
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.totalItemsCount(), 3);
      assert.equal(this.dataController.items().length, 3);
      assert.equal(this.dataController.pageIndex(), 0);
      assert.equal(this.dataSource.pageIndex(), 0);
  });
  
  QUnit.test("Grouping not reset after change option autoExpandAll", function(assert) {
      this.setupDataGridModules({
          dataSource: this.array,
          columns: [{ dataField: 'field1', groupIndex: 0 }, 'field2', 'field3'],
          grouping: { autoExpandAll: true }
      });
  
      this.clock.tick();
  
      assert.equal(this.columnsController.getGroupColumns().length, 1, 'grouped columns count');
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: 'field1', desc: false, isExpanded: true }], 'dataSource group when autoExpandAll true');
  
      //act
      this.option('grouping.autoExpandAll', false);
      this.dataController.optionChanged({ name: 'grouping' });
  
      this.clock.tick();
  
      //assert
      assert.equal(this.columnsController.getGroupColumns().length, 1, 'grouped columns count');
      assert.deepEqual(this.dataController._dataSource.group(), [{ selector: 'field1', desc: false, isExpanded: false }], 'dataSource group when autoExpandAll false');
      assert.equal(this.dataController.totalItemsCount(), 2);
      assert.equal(this.dataController.items().length, 2);
  });
  
  //T231490
  QUnit.test("assign loaded dataSource", function(assert) {
      this.dataSource = new DataSource({
          store: this.array,
          pageSize: 3
      });
  
      this.dataSource.load();
  
      this.setupDataGridModules({
          dataSource: this.dataSource
      });
  
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.items().length, 3, "items count");
      assert.equal(this.dataController.totalCount(), 5, "total count");
      assert.equal(this.dataController.pageCount(), 2, "page count");
  });
  
  QUnit.module("Exporting", {
      beforeEach: function() {
          this.array = [
                  { field1: 1, field2: 2, field3: 3 },
                  { field1: 1, field2: 2, field3: 4 },
                  { field1: 2, field2: 2, field3: 5 },
                  { field1: 2, field2: 3, field3: 6 },
                  { field1: 2, field2: 3, field3: 7 }
          ];
  
          this.setupDataGridModules = function(options) {
              setupDataGridModules(this, ['data', 'virtualScrolling', 'columns', 'filterRow', 'search', 'editing', 'grouping', 'masterDetail', 'summary'], {
                  initDefaultOptions: true,
                  options: options
              });
          };
  
          this.applyOptions = function(options) {
              $.extend(this.options, options);
              this.columnsController.init();
          };
  
          this.clock = sinon.useFakeTimers();
      },
      afterEach: function() {
          this.dispose();
          this.clock.restore();
      }
  });
  
  QUnit.test("loadAll when no dataSource", function(assert) {
      var allItems;
      this.setupDataGridModules({});
  
      this.clock.tick();
  
      //act
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 0, 'items count');
  
      assert.deepEqual(allItems, [], 'all items is empty array');
  });
  
  QUnit.test("loadAll when error on loading", function(assert) {
      var dataErrorOccurredArgs = [],
          error,
          changedArgs = [];
      this.setupDataGridModules({
          dataSource: {
              load: function() {
                  return $.Deferred().reject("Test Error");
              }
          }
      });
  
      this.clock.tick();
  
      this.dataController.dataErrorOccurred.add(function(e) {
          dataErrorOccurredArgs.push(e);
      });
  
      this.dataController.changed.add(function(e) {
          changedArgs.push(e);
      });
  
      //act
      this.dataController.loadAll().fail(function(e) {
          error = e;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 0, 'items count');
      assert.equal(error.message, "Test Error", 'all items is empty array');
      assert.equal(dataErrorOccurredArgs[0].message, "Test Error", 'all items is empty array');
      assert.equal(changedArgs.length, 1, "changed call count");
      assert.equal(changedArgs[0].changeType, "loadError", "changeType");
      assert.equal(changedArgs[0].error.message, "Test Error", "error message");
  });
  
  
  QUnit.test("loadAll skip paging", function(assert) {
      var allItems,
          changedCallCount = 0;
  
      this.setupDataGridModules({
          dataSource: this.array,
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 3, 'items count');
      assert.deepEqual(this.dataController.pageSize(), 3, 'pageSize');
      assert.deepEqual(this.dataController.pageCount(), 2, 'pageCount');
  
      assert.deepEqual(changedCallCount, 0, 'changed call count');
  
      assert.deepEqual(allItems.length, 5, 'all items count');
      assert.deepEqual(allItems[0].rowType, "data", 'item 0 rowType');
      assert.deepEqual(allItems[0].data, this.array[0], 'item 0 data');
      assert.deepEqual(allItems[0].values, [1, 2, 3], 'item 0 values');
  
      assert.deepEqual(allItems[3].rowType, "data", 'item 3 rowType');
      assert.deepEqual(allItems[3].values, [2, 3, 6], 'item 3 values');
  });
  
  QUnit.test("loadAll grouping when remoteOperations disabled", function(assert) {
      var allItems,
          changedCallCount = 0;
  
      this.setupDataGridModules({
          dataSource: {
              store: this.array,
              group: 'field1'
          },
          remoteOperations: false,
          grouping: {
              autoExpandAll: false
          },
          columns: ['field1', 'field2', 'field3'],
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 2, 'items count');
      assert.deepEqual(this.dataController.pageSize(), 3, 'pageSize');
      assert.deepEqual(this.dataController.pageCount(), 1, 'pageCount');
  
      assert.deepEqual(changedCallCount, 0, 'changed call count');
  
      assert.deepEqual(allItems.length, 7, 'all items count');
      assert.deepEqual(allItems[0].rowType, "group", 'item 0 rowType');
      assert.deepEqual(allItems[0].data.key, 1, 'item 0 data key');
      assert.ok(allItems[0].data.items, 'item 0 data items defined');
      assert.deepEqual(allItems[0].data.items.length, 2, 'item 0 data items count');
      assert.deepEqual(allItems[0].values, [1], 'item 0 values');
  
      assert.deepEqual(allItems[1].rowType, "data", 'item 1 rowType');
      assert.deepEqual(allItems[1].data, this.array[0], 'item 1 data');
      assert.deepEqual(allItems[1].values, [null, 2, 3], 'item 1 values');
  
      assert.deepEqual(allItems[3].rowType, "group", 'item 3 rowType');
      assert.deepEqual(allItems[3].data.key, 2, 'item 3 data key');
      assert.ok(allItems[3].data.items, 'item 3 data items defined');
      assert.deepEqual(allItems[3].data.items.length, 3, 'item 3 data key');
      assert.deepEqual(allItems[3].values, [2], 'item 3 values');
  });
  
  QUnit.test("loadAll grouping when remote filtering/sorting/paging enabled", function(assert) {
      var allItems,
          changedCallCount = 0;
  
      this.setupDataGridModules({
          dataSource: {
              store: this.array.concat(this.array),
              group: 'field1'
          },
          remoteOperations: { filtering: true, sorting: true, paging: true },
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      assert.deepEqual(this.dataController.pageCount(), 4, 'pageCount');
  
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 4, 'items count');
      assert.deepEqual(this.dataController.pageSize(), 3, 'pageSize');
      //T240474
      assert.deepEqual(this.dataController.pageCount(), 4, 'pageCount');
  
      assert.deepEqual(changedCallCount, 0, 'changed call count');
  
      assert.deepEqual(allItems.length, 12, 'all items count');
      assert.deepEqual(allItems[0].rowType, "group", 'item 0 rowType');
      assert.deepEqual(allItems[0].data.key, 1, 'item 0 data key');
      assert.ok(allItems[0].data.items, 'item 0 data items defined');
      assert.deepEqual(allItems[0].data.items.length, 4, 'item 0 data items count');
      assert.deepEqual(allItems[0].values, [1], 'item 0 values');
  
      assert.deepEqual(allItems[1].rowType, "data", 'item 1 rowType');
      assert.deepEqual(allItems[1].data, this.array[0], 'item 1 data');
      assert.deepEqual(allItems[1].values, [null, 2, 3], 'item 1 values');
  
      assert.deepEqual(allItems[5].rowType, "group", 'item 5 rowType');
      assert.deepEqual(allItems[5].data.key, 2, 'item 5 data key');
      assert.ok(allItems[5].data.items, 'item 3 data items defined');
      assert.deepEqual(allItems[5].data.items.length, 6, 'item 5 items count');
      assert.deepEqual(allItems[5].values, [2], 'item 5 values');
  });
  
  QUnit.test("loadAll when remote summary enabled", function(assert) {
      var allItems,
          allSummary,
          changedCallCount = 0;
  
      var loadArgs = [];
  
      this.setupDataGridModules({
          dataSource: {
              load: function(e) {
                  loadArgs.push(e);
  
                  return $.Deferred().resolve([
                      { field1: 1, field2: 2 }, { field1: 3, field2: 4 }],
                      { totalCount: 2, summary: [6] }
                  );
              }
          },
          summary: {
              totalItems: [{
                  column: "field2",
                  summaryType: "sum"
              }]
          },
          remoteOperations: true,
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      assert.deepEqual(this.dataController.pageCount(), 1, 'pageCount');
  
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.loadAll().done(function(items, summary) {
          allItems = items;
          allSummary = summary;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(changedCallCount, 0, 'changed call count');
      assert.deepEqual(allItems.length, 2, 'all items count');
      assert.deepEqual(allSummary, [6], 'all summary');
      assert.deepEqual(loadArgs.length, 2, "load count");
      assert.deepEqual(loadArgs[0].skip, 0, "initial load skip");
      assert.deepEqual(loadArgs[0].take, 3, "initial load take");
      assert.deepEqual(loadArgs[0].totalSummary, [{ selector: "field2", summaryType: "sum" }], "initial load totalSummary");
      assert.deepEqual(loadArgs[0].groupSummary, undefined, "initial load groupSummary is not defined");
      assert.deepEqual(loadArgs[1].skip, undefined, "loadAll skip");
      assert.deepEqual(loadArgs[1].take, undefined, "loadAll load take");
      assert.deepEqual(loadArgs[1].totalSummary, [{ selector: "field2", summaryType: "sum" }], "loadAll totalSummary");
      assert.deepEqual(loadArgs[1].groupSummary, undefined, "loadAll groupSummary is not defined");
  });
  
  //T324247
  QUnit.test("loadAll when remote grouping and summary enabled", function(assert) {
      var allItems,
          allSummary,
          changedCallCount = 0;
  
      var loadArgs = [];
  
      this.setupDataGridModules({
          dataSource: {
              load: function(e) {
                  loadArgs.push(e);
  
                  if(e.group) {
                      return $.Deferred().resolve([
                          { key: 1, summary: [2], items: null, count: 1 }, { key: 3, summary: [4], items: null, count: 1 }],
                          { summary: [4] }
                      );
                  } else {
                      return $.Deferred().resolve([
                          { field1: 1, field2: 2 }, { field1: 3, field2: 4 }],
                          { summary: [4] }
                      );
                  }
              },
              group: "field1"
          },
          grouping: {
              autoExpandAll: false
          },
          summary: {
              groupItems: [{
                  column: "field2",
                  summaryType: "sum"
              }],
              totalItems: [{
                  column: "field1",
                  summaryType: "sum"
              }]
          },
          columns: ["field1", "field2"],
          remoteOperations: true,
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      assert.deepEqual(this.dataController.pageCount(), 1, 'pageCount');
  
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.loadAll().done(function(items, summary) {
          allItems = items;
          allSummary = summary;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(changedCallCount, 0, 'changed call count');
      assert.deepEqual(allItems.length, 4, 'all items count');
      assert.deepEqual(allItems[0].data, { key: 1, count: 1, items: [{ field1: 1, field2: 2 }], summary: [2] }, 'item 0');
      assert.deepEqual(allItems[1].data, { field1: 1, field2: 2 }, 'item 1');
      assert.deepEqual(allItems[2].data, { key: 3, count: 1, items: [{ field1: 3, field2: 4 }], summary: [4] }, 'item 2');
      assert.deepEqual(allItems[3].data, { field1: 3, field2: 4 }, 'item 3');
      assert.deepEqual(allSummary, [4], 'all summary exists');
      assert.deepEqual(loadArgs.length, 3, "load count");
      assert.deepEqual(loadArgs[0].skip, undefined, "initial load skip");
      assert.deepEqual(loadArgs[0].take, undefined, "initial load take");
      assert.deepEqual(loadArgs[0].group, [{ selector: "field1", isExpanded: false, desc: false }], "initial load group");
      assert.deepEqual(loadArgs[0].totalSummary, [{ selector: "field1", summaryType: "sum" }], "initial load totalSummary");
      assert.deepEqual(loadArgs[0].groupSummary, [{ selector: "field2", summaryType: "sum" }], "initial load groupSummary");
      assert.deepEqual(loadArgs[1].skip, undefined, "loadAll skip");
      assert.deepEqual(loadArgs[1].take, undefined, "loadAll load take");
      assert.deepEqual(loadArgs[1].group, [{ selector: "field1", isExpanded: false, desc: false }], "loadAll load group");
      assert.deepEqual(loadArgs[1].totalSummary, [{ selector: "field1", summaryType: "sum" }], "loadAll totalSummary");
      assert.deepEqual(loadArgs[1].groupSummary, [{ selector: "field2", summaryType: "sum" }], "loadAll groupSummary");
      assert.deepEqual(loadArgs[2].skip, undefined, "loadAll skip");
      assert.deepEqual(loadArgs[2].take, undefined, "loadAll load take");
      assert.deepEqual(loadArgs[2].group, null, "loadAll load group");
      assert.deepEqual(loadArgs[2].totalSummary, [{ selector: "field1", summaryType: "sum" }], "loadAll totalSummary");
      assert.deepEqual(loadArgs[2].groupSummary, [{ selector: "field2", summaryType: "sum" }], "loadAll groupSummary");
  });
  
  //T437259, T433659
  QUnit.test("loadAll by selected items when remote grouping and summary enabled", function(assert) {
      var allItems,
          allSummary,
          changedCallCount = 0;
  
      var loadArgs = [];
  
      this.setupDataGridModules({
          dataSource: {
              load: function(e) {
                  loadArgs.push(e);
  
                  if(e.group) {
                      return $.Deferred().resolve([
                          { key: 1, summary: [2], items: null, count: 1 }, { key: 3, summary: [4], items: null, count: 1 }],
                          { summary: [4] }
                      );
                  } else {
                      return $.Deferred().resolve([
                          { field1: 1, field2: 2 }, { field1: 3, field2: 4 }],
                          { summary: [4] }
                      );
                  }
              },
              group: "field1"
          },
          grouping: {
              autoExpandAll: false
          },
          summary: {
              groupItems: [{
                  column: "field2",
                  summaryType: "sum"
              }],
              totalItems: [{
                  column: "field1",
                  summaryType: "sum"
              }]
          },
          columns: ["field1", "field2"],
          remoteOperations: true,
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      assert.deepEqual(this.dataController.pageCount(), 1, 'pageCount');
  
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      loadArgs = [];
  
      this.dataController.loadAll([{ field1: 1, field2: 2 }]).done(function(items, summary) {
          allItems = items;
          allSummary = summary;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(loadArgs.length, 0, "load count");
      assert.deepEqual(changedCallCount, 0, 'changed call count');
      assert.deepEqual(allItems.length, 2, 'all items count');
      assert.deepEqual(allItems[0].data, { key: 1, items: [{ field1: 1, field2: 2 }], aggregates: [2] }, 'item 0');
      assert.deepEqual(allItems[1].data, { field1: 1, field2: 2 }, 'item 1');
      assert.deepEqual(allSummary, [1], 'all summary exists');
      //T433659
      assert.deepEqual(this.dataController.getTotalSummaryValue("field1"), 4);
  });
  
  QUnit.test("Skip detail row when loadAll is applied", function(assert) {
      var allItems;
  
      this.setupDataGridModules({
          dataSource: this.array,
          masterDetail: {
              enabled: true,
              template: function() { }
          },
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      //act
      this.dataController.expandRow(this.array[0]);
  
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.equal(this.dataController.items()[1].rowType, "detail", "detail row in original items");
  
      assert.deepEqual(allItems.length, 5, 'all items count');
      assert.deepEqual(allItems[0].rowType, "data", 'item 0 rowType');
      assert.deepEqual(allItems[1].rowType, "data", 'item 1 rowType');
      assert.deepEqual(allItems[2].rowType, "data", 'item 2 rowType');
      assert.deepEqual(allItems[3].rowType, "data", 'item 3 rowType');
      assert.deepEqual(allItems[4].rowType, "data", 'item 4 rowType');
  });
  
  QUnit.test("LoadAll and modified values", function(assert) {
      var allItems;
  
      this.setupDataGridModules({
          dataSource: this.array,
          editing: {
              mode: "batch"
          },
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      //act
      this.editingController.updateFieldValue({
          key: this.array[0],
          column: {
              setCellValue: function(data, value) { data.field1 = value; }
          }
      }, 13);
  
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(allItems.length, 5, 'all items count');
      assert.deepEqual(allItems[0].data, { field1: 13, field2: 2, field3: 3 }, 'item 0 data');
      assert.deepEqual(allItems[0].values, [13, 2, 3], 'item 0 values');
  });
  
  QUnit.test("LoadAll and filtering", function(assert) {
      var allItems;
  
      this.setupDataGridModules({
          dataSource: this.array,
          paging: {
              pageSize: 3
          },
          columns: ["field1", { dataField: "field2", filterValue: 3 }, "field3"]
      });
  
      this.clock.tick();
  
      //act
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(allItems.length, 2, 'all items count');
      assert.deepEqual(allItems[0].data, this.array[3], 'item 0 data');
      assert.deepEqual(allItems[1].data, this.array[4], 'item 1 data');
  });
  
  QUnit.test("LoadAll and grouped field with dataType", function(assert) {
      var allItems;
  
      var array = [{ group: "1", id: 1 }, { group: "1", id: 2 }];
      this.setupDataGridModules({
          dataSource: array,
          columns: [{ dataField: "group", dataType: "number", groupIndex: 0 }, "id"]
      });
  
      this.clock.tick();
  
      //act
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(allItems.length, 3, 'all items count');
      assert.deepEqual(allItems[0].key, [1], 'group row key');
      assert.deepEqual(allItems[0].data, { key: 1, items: array }, 'group row data');
  });
  
  QUnit.test("loadAll during data loading", function(assert) {
      var isLoadAllFailed;
  
      this.setupDataGridModules({
          dataSource: this.array,
          paging: {
              pageSize: 3
          }
      });
  
  
      //act
      this.dataController.loadAll().fail(function() {
          isLoadAllFailed = true;
      });
  
      this.clock.tick();
  
      //assert
      assert.ok(isLoadAllFailed, "loadAll failed");
      assert.equal(this.dataController.items().length, 3, 'items count');
      assert.ok(!this.dataController.isLoading(), 'no loading');
  });
  
  QUnit.test("data loading during loadAll", function(assert) {
      var isLoadAllFailed,
          allItems;
  
      this.setupDataGridModules({
          dataSource: this.array,
          paging: {
              pageSize: 3
          },
          columns: ["field1", "field2", "field3"]
      });
  
      this.clock.tick();
  
  
      //act
      this.dataController.loadAll().fail(function() {
          isLoadAllFailed = true;
      }).done(function(data) {
          allItems = data;
      });
  
      this.dataController.pageIndex(1);
  
      this.clock.tick();
  
      //assert
      assert.ok(!isLoadAllFailed, "loadAll is not failed");
      assert.equal(allItems.length, 5, 'items count');
      assert.equal(this.dataController.pageIndex(), 1, "pageIndex");
      assert.equal(this.dataController.items().length, 2, 'items count');
      assert.deepEqual(this.dataController.items()[0].data, this.array[3], 'item 0 data from second page');
      assert.ok(!this.dataController.isLoading(), 'no loading');
  });
  
  QUnit.test("LoadAll when scrolling mode is virtual", function(assert) {
      var allItems;
  
      this.setupDataGridModules({
          dataSource: this.array,
          scrolling: {
              mode: "virtual"
          },
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      //act
      this.dataController.loadAll().done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(allItems.length, 5, 'all items count');
      assert.deepEqual(allItems[0].data, { field1: 1, field2: 2, field3: 3 }, 'item 0 data');
      assert.deepEqual(allItems[0].values, [1, 2, 3], 'item 0 values');
  });
  
  QUnit.test("Load ALL with data parameter", function(assert) {
      var allItems,
          changedCallCount = 0;
  
      this.setupDataGridModules({
          dataSource: this.array,
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.loadAll([this.array[1], this.array[3]]).done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 3, 'items count');
      assert.deepEqual(this.dataController.pageSize(), 3, 'pageSize');
      assert.deepEqual(this.dataController.pageCount(), 2, 'pageCount');
  
      assert.deepEqual(changedCallCount, 0, 'changed call count');
  
      assert.deepEqual(allItems.length, 2, 'all items count');
      assert.deepEqual(allItems[0].rowType, "data", 'item 0 rowType');
      assert.deepEqual(allItems[0].data, this.array[1], 'item 0 data');
      assert.deepEqual(allItems[0].values, [1, 2, 4], 'item 0 values');
  
      assert.deepEqual(allItems[1].rowType, "data", 'item 1 rowType');
      assert.deepEqual(allItems[1].data, this.array[3], 'item 1 rowType');
      assert.deepEqual(allItems[1].values, [2, 3, 6], 'item 1 values');
  });
  
  QUnit.test("loadAll with data parameter and grouping", function(assert) {
      var allItems,
          changedCallCount = 0;
  
      this.setupDataGridModules({
          dataSource: {
              store: this.array,
              group: 'field1'
          },
          remoteOperations: false,
          grouping: {
              autoExpandAll: false
          },
          columns: ['field1', 'field2', 'field3'],
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      this.dataController.changed.add(function() {
          changedCallCount++;
      });
  
      //act
      this.dataController.loadAll([this.array[0], this.array[3], this.array[4]]).done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(this.dataController.items().length, 2, 'items count');
      assert.deepEqual(this.dataController.pageSize(), 3, 'pageSize');
      assert.deepEqual(this.dataController.pageCount(), 1, 'pageCount');
  
      assert.deepEqual(changedCallCount, 0, 'changed call count');
  
      assert.deepEqual(allItems.length, 5, 'all items count');
  
      assert.deepEqual(allItems[0].rowType, "group", 'rowType');
      assert.deepEqual(allItems[0].data.key, 1, 'data key');
      assert.ok(allItems[0].data.items, 'data items');
      assert.deepEqual(allItems[0].data.items.length, 1, 'data items count');
      assert.deepEqual(allItems[0].values, [1], 'values');
  
      assert.deepEqual(allItems[1].rowType, "data", 'rowType');
      assert.deepEqual(allItems[1].data, this.array[0], 'data');
      assert.deepEqual(allItems[1].values, [null, 2, 3], 'values');
  
      assert.deepEqual(allItems[2].rowType, "group", 'rowType');
      assert.deepEqual(allItems[2].data.key, 2, 'data key');
      assert.ok(allItems[2].data.items, 'data items');
      assert.deepEqual(allItems[2].data.items.length, 2, 'data items count');
      assert.deepEqual(allItems[2].values, [2], 'values');
  
      assert.deepEqual(allItems[3].rowType, "data", 'rowType');
      assert.deepEqual(allItems[3].data, this.array[3], 'data');
      assert.deepEqual(allItems[3].values, [null, 3, 6], 'values');
  
      assert.deepEqual(allItems[4].rowType, "data", 'rowType');
      assert.deepEqual(allItems[4].data, this.array[4], 'data');
      assert.deepEqual(allItems[4].values, [null, 3, 7], 'values');
  });
  
  QUnit.test("LoadAll with data parameter and modified values", function(assert) {
      var allItems;
  
      this.setupDataGridModules({
          dataSource: this.array,
          editing: {
              mode: "batch"
          },
          paging: {
              pageSize: 3
          }
      });
  
      this.clock.tick();
  
      //act
      this.editingController.updateFieldValue({
          key: this.array[0],
          column: {
              setCellValue: function(data, value) { data.field1 = value; }
          }
      }, 13);
  
      this.dataController.loadAll([this.array[0], this.array[1]]).done(function(items) {
          allItems = items;
      });
  
      this.clock.tick();
  
      //assert
      assert.deepEqual(allItems.length, 2, 'all items count');
      assert.deepEqual(allItems[0].data, { field1: 13, field2: 2, field3: 3 }, 'item 0 data');
      assert.deepEqual(allItems[0].values, [13, 2, 3], 'item 0 values');
      assert.deepEqual(allItems[1].data, { field1: 1, field2: 2, field3: 4 }, 'item 0 data');
      assert.deepEqual(allItems[1].values, [1, 2, 4], 'item 1 values');
  });
  