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
JavaScript
/**
* Developer: Stepan Burguchev
* Date: 6/14/2016
* Copyright: 2009-2016 Comindware®
* All Rights Reserved
* Published under the MIT license
*/
;
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
]);
});
});
});