ampersand-view
Version:
A smart base view for Backbone apps, to make it easy to bind collections and properties to the DOM.
250 lines (226 loc) • 6.84 kB
JavaScript
var test = require('tape');
var AmpersandModel = require('ampersand-model');
var AmpersandCollection = require('ampersand-rest-collection');
var AmpersandView = require('../ampersand-view');
var Model = AmpersandModel.extend({
props: {
id: 'number',
name: ['string', true],
html: 'string',
url: 'string',
something: 'string',
fireDanger: 'string'
},
session: {
active: 'boolean'
},
derived: {
classes: {
deps: ['something', 'fireDanger', 'active'],
fn: function () {
return this.something + this.active;
}
}
}
});
var Collection = AmpersandCollection.extend({
model: Model
});
var ItemView = AmpersandView.extend({
template: '<li><a href=""></a><span></span><input/></li>',
initialize: function () {
// register a misc handler so we can test release
this.listenTo(this.model, 'change:something', function () {});
},
render: function () {
this.renderWithTemplate();
this.el.id = '_' + this.model.id;
return this;
}
});
var MainView = AmpersandView.extend({
initialize: function () {
this.el = document.createElement('div');
this.el.id = 'container';
this.collection = new Collection(getModelData());
},
render: function (opts) {
this.el.innerHTML = '<ul></ul>';
this.collectionView = this.renderCollection(this.collection, ItemView, this.query('ul'), opts);
return this;
},
numberRendered: function () {
return this.queryAll('li').length;
}
});
var MainViewUl = MainView.extend({
initialize: function () {
this.el = document.createElement('ul');
this.collection = new Collection(getModelData());
},
render: function (opts) {
this.collectionView = this.renderCollection(this.collection, ItemView);
return this;
}
});
function getModelData() {
return [
'Henrik Joreteg',
'Bugs Bunny',
'Scrooge McDuck',
'Crazy Dave',
'Arty Cee'
].map(function (name, count) {
return {
id: ++count,
avatar: 'http://robohash.org/' + name.charAt(1),
randomHtml: '<p>yo</p>',
name: name,
active: count === 2
};
});
}
test('test initial render', function (t) {
var view = new MainView();
view.render();
t.equal(view.collection.length, 5);
t.equal(view.numberRendered(), view.collection.length);
t.end();
});
test('collection view is returned', function (t) {
var view = new MainView();
view.render();
t.equal(typeof view.collectionView, 'object');
t.equal(view.collectionView.collection.length, 5);
t.end();
});
test('this.el is default', function (t) {
var view = new MainViewUl();
view.render();
t.equal(view.collection.length, 5);
t.equal(view.numberRendered(), view.collection.length);
t.end();
});
test('add', function (t) {
var view = new MainView();
view.render();
view.collection.add({id: 6});
t.equal(view.numberRendered(), view.collection.length);
t.end();
});
test('remove', function (t) {
var view = new MainView();
view.render();
view.collection.remove(view.collection.last());
t.equal(view.numberRendered(), view.collection.length);
t.end();
});
test('reset', function (t) {
var view = new MainView();
view.render();
view.collection.reset();
t.equal(view.numberRendered(), view.collection.length);
t.equal(view.numberRendered(), 0);
t.end();
});
test('sort', function (t) {
var view = new MainView();
view.render();
view.collection.comparator = function (model) {
return model.get('name');
};
view.collection.sort();
t.equal(view.numberRendered(), view.collection.length);
var domIds = [];
view.queryAll('li').forEach(function (el) {
domIds.push(Number(el.id.slice(1)));
});
t.deepEqual(domIds, [5, 2, 4, 1, 3]);
t.end();
});
test('animateRemove', function (t) {
var view = new MainView();
view.render();
var prevAnimateRemove = ItemView.prototype.animateRemove;
ItemView.prototype.animateRemove = function () {
var self = this;
this.el.className = 'fadeOut';
setTimeout(function () {
self.remove();
}, 100);
t.ok('animateRemove called');
};
view.collection.remove(view.collection.last());
setTimeout(function () {
t.equal(view.numberRendered(), view.collection.length);
// set it back
ItemView.prototype.animateRemove = prevAnimateRemove;
t.end();
}, 150);
});
test('filtered', function (t) {
var view = new MainView();
view.render({
filter: function (model) {
return model.get('name').length > 10;
}
});
t.equal(view.numberRendered(), 2);
t.end();
});
test('reversed', function (t) {
var view = new MainView();
view.render({
reverse: true
});
var domIds = [];
view.queryAll('li').forEach(function (el) {
domIds.push(Number(el.id.slice(1)));
});
t.deepEqual(domIds, [5, 4, 3, 2, 1]);
t.end();
});
test('cleanup', function (t) {
var view = new MainView();
view.render();
t.equal(view.numberRendered(), view.collection.length);
t.equal(view.collection.first()._events['change:something'].length, 1);
view.remove();
// when main view is removed so should registered event handler
// from subview
t.notOk(view.collection.first()._events['change:something']);
t.end();
});
test('child view can choose to insert self', function (t) {
var view = new MainView();
ItemView.prototype.insertSelf = true;
ItemView.prototype.render = function (extraInfo) {
t.ok(extraInfo.containerEl);
this.renderWithTemplate();
};
view.render();
t.equal(view.numberRendered(), 0, 'Parent should not have rendered anything');
view.remove();
t.end();
});
test('child view `parent` should be parent view not collection view, when using `renderCollection()`', function (t) {
var Child = AmpersandView.extend({
template: '<li></li>',
initialize: function () {
t.equal(this.parent, view);
t.end();
}
});
var View = AmpersandView.extend({
initialize: function () {
this.el = document.createElement('div');
this.collection = new Collection([{id: 9}]);
},
render: function (opts) {
this.el.innerHTML = '<ul></ul>';
this.collectionView = this.renderCollection(this.collection, Child, this.query('ul'), {parent: this});
}
});
var view = new View();
view.render();
});