UNPKG

comindware.ui

Version:

Comindware Core UI provides the basic components like editors, lists, dropdowns, popups that we so desperately need while creating Marionette-based single-page applications.

640 lines (573 loc) 23.4 kB
/** * Developer: Stepan Burguchev * Date: 6/14/2016 * Copyright: 2009-2016 Comindware® * All Rights Reserved * Published under the MIT license */ "use strict"; import Chance from 'chance'; import core from 'coreApi'; import { expectCollectionsToBeEqual, expectToHaveSameMembers } from '../utils/helpers'; import { TaskModel, addChanceMixins } from '../utils/testData'; let chance = new Chance(); let repository = addChanceMixins(chance); describe('VirtualCollection', function () { var assigneeGrouping = { modelFactory: function (model) { return model.get('assignee'); }, comparator: function (model) { return model.get('assignee').id; }, iterator: function (model) { return model.get('assignee').get('name'); }, affectedAttributes: [ 'assignee' ] }; function generateTask(attributes) { return new TaskModel(chance.task(attributes)); } function generateTaskArray(len, fn) { if (!fn) { fn = () => {}; } return _.times(len, n => generateTask(fn(n))); } function createFixture(list, virtualCollectionOptions, collectionOptions) { let options = _.extend({ model: TaskModel }, collectionOptions); let collection = new Backbone.Collection(list, options); let virtualCollection = new core.collections.VirtualCollection(collection, virtualCollectionOptions); return { collection, virtualCollection }; } describe('When grouping tree collection', function () { it('should apply comparator to all levels of the tree', function () { let count = 3; let tasks = generateTaskArray(count, n => ({ assignee: repository.users[n % 2], title: String(count - n) })); tasks[1].children = new Backbone.Collection([ generateTask({ title: '2' }), generateTask({ title: '1' }) ]); let { collection, virtualCollection } = createFixture(tasks, { grouping: [ assigneeGrouping ], comparator: function (model) { return model.get('title'); } }); // all levels of the tree must be in correct order expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(2), collection.at(0), virtualCollection.at(3), collection.at(1), collection.at(1).children.at(0), collection.at(1).children.at(1) ]); }); }); describe('When no grouping is set', function () { it('should pass through default collection', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(50)); expectCollectionsToBeEqual(virtualCollection, collection); }); }); describe('When grouping plain list', function () { it('should group by iterator', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(4, n => ({ assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ] }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), collection.at(2), virtualCollection.at(3), collection.at(1), collection.at(3) ]); }); it('should sort groups with comparator', function () { let user1 = chance.user({ name: 'Ken' }); let user2 = chance.user({ name: 'Ben' }); let { virtualCollection } = createFixture(generateTaskArray(4, n => ({ assignee: n % 2 ? user1 : user2 })), { grouping: [ { modelFactory: function (model) { return model.get('assignee'); }, comparator: function (model) { return model.get('assignee').get('name'); }, iterator: function (model) { return model.get('assignee').get('id'); } } ] }); expect(virtualCollection.at(0).id).toEqual(user2.id, 'Ben goes first'); expect(virtualCollection.at(3).id).toEqual(user1.id, 'Then goes Ken'); }); it('should sort items within a group with comparator function', function () { let count = 4; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n), assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ], comparator: function (model) { return model.get('title'); } }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(2), collection.at(0), virtualCollection.at(3), collection.at(3), collection.at(1) ]); }); it('should accept group iterator as a model attrubute name', function () { let count = 2; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n) })), { grouping: [ { modelFactory: function (model) { return new Backbone.Model({ title: model.get('title') }); }, comparator: function (model) { return model.get('title'); }, iterator: 'title' } ] }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(1), virtualCollection.at(2), collection.at(0) ]); }); it('should accept group comparator as a model attrubute name', function () { let count = 2; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n) })), { grouping: [ { modelFactory: function (model) { return new Backbone.Model({ title: model.get('title') }); }, comparator: 'title', iterator: 'title' } ] }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(1), virtualCollection.at(2), collection.at(0) ]); }); it('should accept group modelFactory as a model attrubute name', function () { let count = 2; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n) })), { grouping: [ { modelFactory: 'title', comparator: 'title', iterator: 'title' } ] }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(1), virtualCollection.at(2), collection.at(0) ]); expect(virtualCollection.at(0).get('displayText')).toEqual(collection.at(1).get('title')); expect(virtualCollection.at(0).get('groupingModel')).toEqual(true); }); it('should be able to omit modelFactory and comparator', function () { let count = 2; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n) })), { grouping: [ { iterator: 'title' } ] }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(1), virtualCollection.at(2), collection.at(0) ]); expect(virtualCollection.at(0).get('displayText')).toEqual(collection.at(1).get('title')); expect(virtualCollection.at(0).get('groupingModel')).toEqual(true); }); it('should compute affected attributes from field based options', function () { let count = 2; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n) })), { grouping: [ { iterator: 'title' } ] }); collection.at(0).set('title', 'synthetic title 0'); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), virtualCollection.at(2), collection.at(1) ]); }); }); describe('When changing a model', function () { it('should update grouping on affected attribute change', function () { let count = 4; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ] }); let resetCallback = jasmine.createSpy('resetCallback'); let addCallback = jasmine.createSpy('addCallback'); let removeCallback = jasmine.createSpy('removeCallback'); virtualCollection.on('reset', resetCallback); virtualCollection.on('add', addCallback); virtualCollection.on('remove', removeCallback); collection.at(0).set('assignee', repository.users[1]); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(2), virtualCollection.at(2), collection.at(0), collection.at(1), collection.at(3) ]); expect(resetCallback).toHaveBeenCalledTimes(1); expect(addCallback).not.toHaveBeenCalled(); expect(removeCallback).not.toHaveBeenCalled(); }); it('should update sorting on affected attribute change', function () { let count = 4; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n), assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ], comparator: function (model) { return model.get('title'); } }); let resetCallback = jasmine.createSpy('resetCallback'); let addCallback = jasmine.createSpy('addCallback'); let removeCallback = jasmine.createSpy('removeCallback'); virtualCollection.on('reset', resetCallback); virtualCollection.on('add', addCallback); virtualCollection.on('remove', removeCallback); collection.at(0).set('title', 'synthetic title 0'); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), collection.at(2), virtualCollection.at(3), collection.at(3), collection.at(1) ]); expect(resetCallback).toHaveBeenCalledTimes(1); expect(addCallback).not.toHaveBeenCalled(); expect(removeCallback).not.toHaveBeenCalled(); }); }); describe('When resetting parent collection', function () { it('should reflect the changes', function () { // Fixture setup let { collection, virtualCollection } = createFixture(generateTaskArray(4, n => ({ assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ] }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), collection.at(2), virtualCollection.at(3), collection.at(1), collection.at(3) ]); // Exercise system collection.reset(generateTaskArray(4, n => ({ assignee: repository.users[n % 2] }))); // Verify outcome expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), collection.at(2), virtualCollection.at(3), collection.at(1), collection.at(3) ]); }); }); describe('When changing parent collection order', function () { it('should reflect the changes on leaf level', function () { // Fixture setup let count = 4; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ assignee: repository.users[n % 2], title: 'some title ' + (count - n) })), { grouping: [ assigneeGrouping ] }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), collection.at(2), virtualCollection.at(3), collection.at(1), collection.at(3) ]); // Exercise system collection.comparator = function (model) { return model.get('title'); }; collection.sort(); // Verify outcome expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(1), collection.at(3), virtualCollection.at(3), collection.at(0), collection.at(2) ]); }); }); describe('When filtering collection', function () { it('should filter grouped list', function () { // Fixture setup and system exercise let count = 4; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ], filter: function (model) { return model.get('assignee') === repository.users[1]; } }); // Verify outcome expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(1), collection.at(3) ]); }); }); describe('When getting single value', function () { it('should return it by id index', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(10), { grouping: [ assigneeGrouping ] }); let expectedModel = collection.at(0); let actualModel = virtualCollection.get(expectedModel.id); expect(expectedModel).toEqual(actualModel); }); }); describe('When removing item', function () { it('should remove item with full reset', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(3)); let resetCallback = jasmine.createSpy('resetCallback'); let addCallback = jasmine.createSpy('addCallback'); let removeCallback = jasmine.createSpy('removeCallback'); virtualCollection.on('reset', resetCallback); virtualCollection.on('add', addCallback); virtualCollection.on('remove', removeCallback); collection.remove(collection.at(1)); expectToHaveSameMembers(virtualCollection.models, collection.models); expect(resetCallback).toHaveBeenCalledTimes(1); expect(addCallback).not.toHaveBeenCalled(); expect(removeCallback).not.toHaveBeenCalled(); }); it('should remove empty parent groups', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(3, n => ({ assignee: repository.users[n] })), { grouping: [ assigneeGrouping ] }); collection.remove(collection.at(1)); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), virtualCollection.at(2), collection.at(1) ]); }); it('should not remove parent groups if it is not empty', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(4, n => ({ assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ] }); collection.remove(collection.at(1)); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), collection.at(1), virtualCollection.at(3), collection.at(2) ]); }); }); describe('When adding item', function () { it('should add item with full reset', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(3), { delayedAdd: false }); let newTask = generateTask(); let resetCallback = jasmine.createSpy('resetCallback'); let addCallback = jasmine.createSpy('addCallback'); let removeCallback = jasmine.createSpy('removeCallback'); virtualCollection.on('reset', resetCallback); virtualCollection.on('add', addCallback); virtualCollection.on('remove', removeCallback); collection.add(newTask); expectToHaveSameMembers(virtualCollection.models, collection.models); expect(resetCallback).toHaveBeenCalledTimes(1); expect(addCallback).not.toHaveBeenCalled(); expect(removeCallback).not.toHaveBeenCalled(); }); it('should add missing parent groups', function () { let { collection, virtualCollection } = createFixture(generateTaskArray(2, n => ({ assignee: repository.users[n] })), { grouping: [ assigneeGrouping ], delayedAdd: false }); let newTask = generateTask({ assignee: repository.users[2] }); collection.add(newTask); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(0), virtualCollection.at(2), collection.at(1), virtualCollection.at(4), collection.at(2) ]); }); it('should add item at exact position', function () { let count = 4; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n), assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ], comparator: function (model) { return model.get('title'); }, delayedAdd: false }); let newTask = generateTask({ assignee: repository.users[0], title: "synthetic title 0" }); virtualCollection.add(newTask, { at: 6 }); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(2), collection.at(0), virtualCollection.at(3), collection.at(3), collection.at(1), newTask ]); }); it('should update internal index while adding item at exact position', function () { let count = 4; let { collection, virtualCollection } = createFixture(generateTaskArray(count, n => ({ title: 'synthetic title ' + (count - n), assignee: repository.users[n % 2] })), { grouping: [ assigneeGrouping ], comparator: function (model) { return model.get('title'); }, delayedAdd: false }); let newTask = generateTask({ assignee: repository.users[0], title: "synthetic title 0" }); virtualCollection.add(newTask, { at: 6 }); virtualCollection.__rebuildModels(); expectCollectionsToBeEqual(virtualCollection, [ virtualCollection.at(0), collection.at(2), collection.at(0), virtualCollection.at(3), collection.at(3), collection.at(1), newTask ]); }); }); });