UNPKG

11.2 kBJavaScriptView Raw
1/*global document*/
2var test = require('tape');
3var AmpCollection = require('ampersand-collection');
4var AmpModel = require('ampersand-model');
5var AmpView = require('ampersand-view');
6var CollectionView = require('../ampersand-collection-view');
7
8
9// test data
10var data = [
11 {id: 1, name: 'mary', age: 20},
12 {id: 2, name: 'sue', age: 25},
13 {id: 3, name: 'dave', age: 30}
14];
15
16// item model
17var Person = AmpModel.extend({
18 props: {id: 'number', name: 'string', age: 'number'},
19 derived: {
20 isStillYoung: {
21 deps: ['age'],
22 fn: function () {
23 return this.age <= 20;
24 }
25 }
26 }
27});
28
29// collection for that model
30var Collection = AmpCollection.extend({
31 model: Person,
32 last: function () {
33 return this.models[this.models.length - 1];
34 },
35 first: function () {
36 return this.models[0];
37 }
38});
39
40var ItemView = AmpView.extend({
41 template: '<div></div>',
42 bindings: {
43 'model.name': ''
44 },
45 render: function () {
46 this.renderWithTemplate();
47 this.el.id = '_' + this.model.id;
48 return this;
49 }
50});
51
52var AlternativeItemView = AmpView.extend({
53 template: '<span></span>',
54 bindings: {
55 'model.name': ''
56 }
57});
58
59var MainView = AmpView.extend({
60 initialize: function () {
61 this.el = document.createElement('div');
62 this.el.id = 'container';
63 this.collection = new Collection(data);
64 },
65 render: function (opts) {
66 this.el.innerHTML = '<ul></ul>';
67 this.renderCollection(this.collection, ItemView, this.query('ul'), opts);
68 return this;
69 }
70});
71
72function getRendered(view) {
73 return Array.prototype.slice.call(view.el.querySelectorAll('div'));
74}
75
76function numberRendered(view) {
77 return getRendered(view).length;
78}
79
80test('should render all when calling `render`', function (t) {
81 var coll = new Collection(data);
82 var div = document.createElement('div');
83 var cv = new CollectionView({
84 el: div,
85 collection: coll,
86 view: ItemView
87 });
88 t.equal(cv.el.innerHTML, '');
89 cv.render();
90 t.equal(cv.el.innerHTML, '<div id="_1">mary</div><div id="_2">sue</div><div id="_3">dave</div>');
91 t.end();
92});
93
94test('should render different view based on a given expression', function (t) {
95 var coll = new Collection(data);
96 var div = document.createElement('div');
97 var cv = new CollectionView({
98 el: div,
99 collection: coll,
100 view: function (options) {
101 if (options.model.isStillYoung) {
102 return new AlternativeItemView(options);
103 }
104
105 return new ItemView(options);
106 }
107 });
108 t.equal(cv.el.innerHTML, '');
109 cv.render();
110 t.equal(cv.el.innerHTML, '<span>mary</span><div id="_2">sue</div><div id="_3">dave</div>');
111 t.end();
112});
113
114test('should call `remove` on view corresponding to removed model', function (t) {
115 var coll = new Collection(data);
116 var div = document.createElement('div');
117 var cv = new CollectionView({
118 el: div,
119 collection: coll,
120 view: ItemView
121 });
122 var count = 0;
123 cv.render();
124 t.equal(cv.views.length, 3);
125 var firstView = cv.views[0];
126 firstView.remove = function () {
127 count++;
128 };
129 coll.remove(coll.at(0));
130 t.equal(cv.views.length, 2);
131 t.equal(count, 1, 'remove should have been called once');
132 t.end();
133});
134
135test('adding to collection should work', function (t) {
136 var coll = new Collection(data);
137 var div = document.createElement('div');
138 var cv = new CollectionView({
139 el: div,
140 collection: coll,
141 view: ItemView
142 });
143 cv.render();
144 var firstView = cv.views[0];
145 var firstEl = firstView && firstView.el;
146
147 coll.add({name: 'henrik', id: 4});
148 t.equal(cv.views.length, 4);
149 t.equal(cv.el.innerHTML, '<div id="_1">mary</div><div id="_2">sue</div><div id="_3">dave</div><div id="_4">henrik</div>');
150 t.equal(cv.views[0], firstView);
151 t.equal(cv.views[0].el, firstEl);
152
153 t.end();
154});
155
156test('adding at specific index should work', function (t) {
157 var coll = new Collection(data);
158 var div = document.createElement('div');
159 var cv = new CollectionView({
160 el: div,
161 collection: coll,
162 view: ItemView
163 });
164 cv.render();
165
166 coll.add({name: 'henrik', id: 4}, { at: 1 });
167 t.equal(cv.el.innerHTML, '<div id="_1">mary</div><div id="_4">henrik</div><div id="_2">sue</div><div id="_3">dave</div>');
168
169 t.end();
170});
171
172test('add', function (t) {
173 var coll = new Collection(data);
174 var div = document.createElement('div');
175 var view = new CollectionView({
176 el: div,
177 collection: coll,
178 view: ItemView
179 });
180 view.render();
181 view.collection.add({id: 6});
182 t.equal(numberRendered(view), view.collection.length);
183 t.end();
184});
185
186test('remove', function (t) {
187 var coll = new Collection(data);
188 var div = document.createElement('div');
189 var view = new CollectionView({
190 el: div,
191 collection: coll,
192 view: ItemView
193 });
194 view.render();
195 view.collection.remove(view.collection.models[view.collection.models.length - 1]);
196 t.equal(numberRendered(view), view.collection.length);
197 t.end();
198});
199
200test('reset', function (t) {
201 var coll = new Collection(data);
202 var div = document.createElement('div');
203 var view = new CollectionView({
204 el: div,
205 collection: coll,
206 view: ItemView
207 });
208 view.render();
209 t.equal(numberRendered(view), 3);
210
211 view.collection.reset();
212 t.equal(numberRendered(view), view.collection.length);
213 t.equal(numberRendered(view), 0);
214
215 view.collection.reset([
216 {id: 10, name: 'mary', age: 20},
217 ]);
218 t.equal(view.collection.length, 1);
219 t.equal(view.views.length, 1);
220 t.equal(numberRendered(view), 1);
221
222 t.end();
223});
224
225test('sort and resort', function (t) {
226 var domIds = [];
227 var coll = new Collection(data);
228 var div = document.createElement('div');
229 var view = new CollectionView({
230 el: div,
231 collection: coll,
232 view: ItemView
233 });
234 view.render();
235
236 // Sort by name
237 view.collection.comparator = function (model) {
238 return model.get('name');
239 };
240 view.collection.sort();
241
242 // All elements rendered and in order of name
243 t.equal(numberRendered(view), view.collection.length);
244 domIds = [];
245 getRendered(view).forEach(function (el) {
246 domIds.push(Number(el.id.slice(1)));
247 });
248 t.deepEqual(domIds, [3, 1, 2]);
249
250 // Sort by age
251 view.collection.comparator = function (model) {
252 return model.get('age');
253 };
254 view.collection.sort();
255
256 // All elements rendered and in order of name
257 t.equal(numberRendered(view), view.collection.length);
258 domIds = [];
259 getRendered(view).forEach(function (el) {
260 domIds.push(Number(el.id.slice(1)));
261 });
262 t.deepEqual(domIds, [1, 2, 3]);
263
264 t.end();
265});
266
267test('sort and resort reversed', function (t) {
268 var domIds = [];
269 var coll = new Collection(data);
270 var div = document.createElement('div');
271 var view = new CollectionView({
272 el: div,
273 collection: coll,
274 view: ItemView,
275 reverse: true
276 });
277 view.render();
278
279 // Sort by name
280 view.collection.comparator = function (model) {
281 return model.get('name');
282 };
283 view.collection.sort();
284
285 // All elements rendered and in order of name
286 t.equal(numberRendered(view), view.collection.length);
287 domIds = [];
288 getRendered(view).forEach(function (el) {
289 domIds.push(Number(el.id.slice(1)));
290 });
291 t.deepEqual(domIds, [2, 1, 3]);
292
293 // Sort by age
294 view.collection.comparator = function (model) {
295 return model.get('age');
296 };
297 view.collection.sort();
298
299 // All elements rendered and in order of name
300 t.equal(numberRendered(view), view.collection.length);
301 domIds = [];
302 getRendered(view).forEach(function (el) {
303 domIds.push(Number(el.id.slice(1)));
304 });
305 t.deepEqual(domIds, [3, 2, 1]);
306
307 t.end();
308});
309
310test('animateRemove', function (t) {
311 var coll = new Collection(data);
312 var div = document.createElement('div');
313 var view = new CollectionView({
314 el: div,
315 collection: coll,
316 view: ItemView
317 });
318 view.render();
319 var prevAnimateRemove = ItemView.prototype.animateRemove;
320 ItemView.prototype.animateRemove = function () {
321 var self = this;
322 this.el.className = 'fadeOut';
323 setTimeout(function () {
324 self.remove();
325 }, 100);
326 t.ok('animateRemove called');
327 };
328 view.collection.remove(view.collection.last());
329 setTimeout(function () {
330 t.equal(numberRendered(view), view.collection.length);
331 // set it back
332 ItemView.prototype.animateRemove = prevAnimateRemove;
333 t.end();
334 }, 150);
335});
336
337test('filtered', function (t) {
338 var view = new MainView();
339 view.render({
340 filter: function (model) {
341 return model.get('name').length > 3;
342 }
343 });
344 t.equal(numberRendered(view), 2);
345 t.end();
346});
347
348test('reversed', function (t) {
349 var view = new MainView();
350 view.render({
351 reverse: true
352 });
353 var domIds = [];
354 getRendered(view).forEach(function (el) {
355 domIds.push(Number(el.id.slice(1)));
356 });
357 t.deepEqual(domIds, [3, 2, 1]);
358 t.end();
359});
360
361test('cleanup', function (t) {
362 var coll = new Collection(data);
363 var div = document.createElement('div');
364 var view = new CollectionView({
365 el: div,
366 collection: coll,
367 view: ItemView
368 });
369 view.render();
370 t.equal(numberRendered(view), view.collection.length);
371 var firstModel = view.collection.first();
372 var firstView = view.views[0];
373 firstView.listenTo(firstModel, 'change:something', function () {});
374 t.equal(view.collection.first()._events['change:something'].length, 1);
375 view.remove();
376 // when main view is removed so should registered event handler
377 // from subview
378 t.notOk(view.collection.first()._events['change:something']);
379 t.end();
380});
381
382test('child view can choose to insert self', function (t) {
383 var view = new MainView();
384 ItemView.prototype.insertSelf = true;
385 ItemView.prototype.render = function (extraInfo) {
386 t.ok(extraInfo.containerEl);
387 this.renderWithTemplate();
388 };
389
390 view.render();
391 t.equal(numberRendered(view), 0, 'Parent should not have rendered anything');
392 view.remove();
393 t.end();
394});
395
396test('helpful exceptions for missing parameters', function (t) {
397 var coll = new Collection(data);
398 var div = document.createElement('div');
399 t.throws(function noParams() {
400 new CollectionView();
401 }, ReferenceError, 'Collection view missing required parameters: collection, el');
402 t.throws(function noCollection() {
403 new CollectionView({ el: div });
404 }, ReferenceError, 'Collection view requires a collection');
405 t.throws(function noCollection() {
406 new CollectionView({ collection: coll });
407 }, ReferenceError, 'Collection view requires an el');
408 t.end();
409});
410