blob: 0ecdd2f800c936180308dd9e66703074ae3d0a0f [file] [log] [blame]
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -07001/**
2 * © OpenCORD
3 *
4 * Created by teone on 3/24/16.
5 */
6
7(function () {
8 'use strict';
9
Matteo Scandolo8cd182a2016-05-06 09:42:45 -070010 var scope, element, isolatedScope, rootScope, compile;
11 const compileElement = () => {
12
13 if(!scope){
14 scope = rootScope.$new();
15 }
16
17 element = angular.element('<xos-table config="config" data="data"></xos-table>');
18 compile(element)(scope);
19 scope.$digest();
20 isolatedScope = element.isolateScope().vm;
21 }
22
23
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070024 describe('The xos.helper module', function(){
25 describe('The xos-table component', () => {
26
27 beforeEach(module('xos.helpers'));
28
Matteo Scandolo8cd182a2016-05-06 09:42:45 -070029 beforeEach(inject(function ($compile, $rootScope) {
30 compile = $compile;
31 rootScope = $rootScope;
32 }));
33
Matteo Scandolobe136072016-06-03 16:58:43 -070034 it('should throw an error if no config is specified', () => {
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070035 function errorFunctionWrapper(){
Matteo Scandolo8cd182a2016-05-06 09:42:45 -070036 compileElement();
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070037 }
38 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a configuration via the "config" attribute'));
Matteo Scandolobe136072016-06-03 16:58:43 -070039 });
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070040
Matteo Scandolobe136072016-06-03 16:58:43 -070041 it('should throw an error if no config columns are specified', () => {
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070042 function errorFunctionWrapper(){
43 // setup the parent scope
Matteo Scandolobe136072016-06-03 16:58:43 -070044 scope = rootScope.$new();
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070045 scope.config = 'green';
Matteo Scandolo8cd182a2016-05-06 09:42:45 -070046 compileElement();
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070047 }
48 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a columns list in the configuration'));
Matteo Scandolobe136072016-06-03 16:58:43 -070049 });
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070050
Matteo Scandolo1cbb96c2016-06-15 13:40:17 -070051 describe('when basically configured', function() {
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070052
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070053 beforeEach(inject(function ($compile, $rootScope) {
Matteo Scandolo8cd182a2016-05-06 09:42:45 -070054
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070055 scope = $rootScope.$new();
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070056
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070057 scope.config = {
58 columns: [
59 {
60 label: 'Label 1',
61 prop: 'label-1'
62 },
63 {
64 label: 'Label 2',
65 prop: 'label-2'
66 }
67 ]
68 };
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070069
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070070 scope.data = [
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070071 {
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070072 'label-1': 'Sample 1.1',
73 'label-2': 'Sample 1.2'
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070074 },
75 {
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070076 'label-1': 'Sample 2.1',
77 'label-2': 'Sample 2.2'
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070078 }
79 ]
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070080
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070081 element = angular.element('<xos-table config="config" data="data"></xos-table>');
82 $compile(element)(scope);
83 scope.$digest();
84 isolatedScope = element.isolateScope().vm;
85 }));
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070086
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070087 it('should contain 2 columns', function() {
88 var th = element[0].getElementsByTagName('th');
89 expect(th.length).toEqual(2);
90 expect(isolatedScope.columns.length).toEqual(2);
91 });
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -070092
Matteo Scandolo6a6586e2016-04-14 16:52:13 -070093 it('should contain 3 rows', function() {
94 var tr = element[0].getElementsByTagName('tr');
95 expect(tr.length).toEqual(3);
96 });
97
Matteo Scandolo8cd182a2016-05-06 09:42:45 -070098 it('should render labels', () => {
99 let label1 = $(element).find('thead tr th')[0]
100 let label2 = $(element).find('thead tr th')[1]
101 expect($(label1).text().trim()).toEqual('Label 1');
102 expect($(label2).text().trim()).toEqual('Label 2');
103 });
104
Matteo Scandolofb5937d2016-04-15 14:27:54 -0700105 describe('when no data are provided', () => {
106 beforeEach(() => {
107 isolatedScope.data = [];
108 scope.$digest();
109 });
110 it('should render an alert', () => {
111 let alert = element[0].getElementsByClassName('alert');
112 let table = element[0].getElementsByTagName('table');
113 expect(alert.length).toEqual(1);
114 expect(table.length).toEqual(1);
115 });
116 });
117
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700118 describe('when a field type is provided', () => {
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700119 describe('and is boolean', () => {
120 beforeEach(() => {
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700121 scope.config = {
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700122 columns: [
123 {
124 label: 'Label 1',
125 prop: 'label-1',
126 type: 'boolean'
Matteo Scandolodee7c6c2016-06-14 17:26:51 -0700127 },
128 {
129 label: 'Label 2',
130 prop: 'label-2',
131 type: 'boolean'
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700132 }
133 ]
134 };
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700135 scope.data = [
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700136 {
Matteo Scandolodee7c6c2016-06-14 17:26:51 -0700137 'label-1': true,
138 'label-2': 1
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700139 },
140 {
Matteo Scandolodee7c6c2016-06-14 17:26:51 -0700141 'label-1': false,
142 'label-2': 0
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700143 }
144 ];
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700145 compileElement();
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700146 });
147
148 it('should render an incon', () => {
149 let td1 = $(element).find('tbody tr:first-child td')[0];
150 let td2 = $(element).find('tbody tr:last-child td')[0];
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700151 expect($(td1).find('i')).toHaveClass('glyphicon-ok');
152 expect($(td2).find('i')).toHaveClass('glyphicon-remove');
153 });
Matteo Scandoloa31617e2016-05-18 14:57:56 -0700154
155 describe('with field filters', () => {
156 beforeEach(() => {
157 scope.config.filter = 'field';
158 compileElement();
159 });
160
161 it('should render a dropdown for filtering', () => {
162 let td1 = $(element).find('table tbody tr td')[0];
Matteo Scandoloa31617e2016-05-18 14:57:56 -0700163 expect(td1).toContainElement('select');
164 expect(td1).not.toContainElement('input');
165 });
Matteo Scandolodee7c6c2016-06-14 17:26:51 -0700166
167 it('should correctly filter results', () => {
168 isolatedScope.query = {
169 'label-1': false
170 };
171 scope.$digest();
172 expect(isolatedScope.query['label-1']).toBeFalsy();
173 var tr = $(element).find('tbody:last-child > tr');
174 var icon = $(tr[0]).find('td i');
175 expect(tr.length).toEqual(1);
176 expect(icon).toHaveClass('glyphicon-remove');
177 });
178
179 it('should correctly filter results if the field is in the form of 0|1', () => {
180 isolatedScope.query = {
181 'label-2': false
182 };
183 scope.$digest();
184 expect(isolatedScope.query['label-1']).toBeFalsy();
185 var tr = $(element).find('tbody:last-child > tr');
186 var icon = $(tr[0]).find('td i');
187 expect(tr.length).toEqual(1);
188 expect(icon).toHaveClass('glyphicon-remove');
189 });
Matteo Scandoloa31617e2016-05-18 14:57:56 -0700190 });
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700191 });
192
193 describe('and is date', () => {
194 beforeEach(() => {
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700195 scope.config = {
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700196 columns: [
197 {
198 label: 'Label 1',
199 prop: 'label-1',
200 type: 'date'
201 }
202 ]
203 };
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700204 scope.data = [
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700205 {
206 'label-1': '2015-02-17T22:06:38.059000Z'
207 }
208 ];
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700209 compileElement();
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700210 });
211
212 it('should render an formatted date', () => {
213 let td1 = $(element).find('tbody tr:first-child td')[0];
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700214 expect($(td1).text().trim()).toEqual('14:06 Feb 17, 2015');
215 });
216 });
217
218 describe('and is array', () => {
219 beforeEach(() => {
220 scope.data = [
221 {categories: ['Film', 'Music']}
222 ];
223 scope.config = {
Matteo Scandolo1cbb96c2016-06-15 13:40:17 -0700224 filter: 'field',
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700225 columns: [
226 {
227 label: 'Categories',
228 prop: 'categories',
229 type: 'array'
230 }
231 ]
232 }
233 compileElement();
234 });
235 it('should render a comma separated list', () => {
Matteo Scandolo1cbb96c2016-06-15 13:40:17 -0700236 let td1 = $(element).find('tbody:last-child tr:first-child')[0];
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700237 expect($(td1).text().trim()).toEqual('Film, Music');
238 });
Matteo Scandolo1cbb96c2016-06-15 13:40:17 -0700239
240 it('should not render the filter field', () => {
241 let filter = $(element).find('tbody tr td')[0];
242 expect($(filter)).not.toContainElement('input');
243 });
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700244 });
245
Matteo Scandoloe5d74562016-05-06 10:08:34 -0700246 describe('and is object', () => {
247 beforeEach(() => {
248 scope.data = [
249 {
250 attributes: {
251 age: 20,
252 height: 50
253 }
254 }
255 ];
256 scope.config = {
Matteo Scandolo1cbb96c2016-06-15 13:40:17 -0700257 filter: 'field',
Matteo Scandoloe5d74562016-05-06 10:08:34 -0700258 columns: [
259 {
260 label: 'Categories',
261 prop: 'attributes',
262 type: 'object'
263 }
264 ]
265 }
266 compileElement();
267 });
268 it('should render a list of key-values', () => {
Matteo Scandolo1cbb96c2016-06-15 13:40:17 -0700269 let td = $(element).find('tbody:last-child tr:first-child')[0];
Matteo Scandolo7ebd1bf2016-05-06 11:39:56 -0700270 let ageLabel = $(td).find('dl dt')[0];
271 let ageValue = $(td).find('dl dd')[0];
272 let heightLabel = $(td).find('dl dt')[1];
273 let heightValue = $(td).find('dl dd')[1];
Matteo Scandoloe5d74562016-05-06 10:08:34 -0700274 expect($(ageLabel).text().trim()).toEqual('age');
275 expect($(ageValue).text().trim()).toEqual('20');
276 expect($(heightLabel).text().trim()).toEqual('height');
277 expect($(heightValue).text().trim()).toEqual('50');
278 });
Matteo Scandolo1cbb96c2016-06-15 13:40:17 -0700279
280 it('should not render the filter field', () => {
281 let filter = $(element).find('tbody tr td')[0];
282 expect($(filter)).not.toContainElement('input');
283 });
Matteo Scandoloe5d74562016-05-06 10:08:34 -0700284 });
285
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700286 describe('and is custom', () => {
Matteo Scandolob4fdd0a2016-05-09 15:27:47 -0700287
288 let formatterFn = jasmine.createSpy('formatter').and.returnValue('Formatted Content');
289
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700290 beforeEach(() => {
291 scope.data = [
292 {categories: ['Film', 'Music']}
293 ];
294 scope.config = {
Matteo Scandolo8a902e12016-06-17 10:04:28 -0700295 filter: 'field',
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700296 columns: [
297 {
298 label: 'Categories',
299 prop: 'categories',
300 type: 'custom',
Matteo Scandolob4fdd0a2016-05-09 15:27:47 -0700301 formatter: formatterFn
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700302 }
303 ]
304 }
305 compileElement();
306 });
307
308 it('should check for a formatter property', () => {
309 function errorFunctionWrapper(){
310 // setup the parent scope
311 scope = rootScope.$new();
312 scope.config = {
313 columns: [
314 {
315 label: 'Categories',
316 prop: 'categories',
317 type: 'custom'
318 }
319 ]
320 };
321 compileElement();
322 }
323 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.'));
324 });
325
Matteo Scandoloabb75622016-05-06 13:09:19 -0700326 it('should check that the formatter property is a function', () => {
327 function errorFunctionWrapper(){
328 // setup the parent scope
329 scope = rootScope.$new();
330 scope.config = {
331 columns: [
332 {
333 label: 'Categories',
334 prop: 'categories',
335 type: 'custom',
336 formatter: 'formatter'
337 }
338 ]
339 };
340 compileElement();
341 }
342 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.'));
343 });
344
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700345 it('should format data using the formatter property', () => {
Matteo Scandolo8a902e12016-06-17 10:04:28 -0700346 let td1 = $(element).find('tbody:last-child tr:first-child')[0];
Matteo Scandolo8cd182a2016-05-06 09:42:45 -0700347 expect($(td1).text().trim()).toEqual('Formatted Content');
Matteo Scandolob4fdd0a2016-05-09 15:27:47 -0700348 // the custom formatted should receive the entire object, otherwise is not so custom
349 expect(formatterFn).toHaveBeenCalledWith({categories: ['Film', 'Music']});
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700350 });
Matteo Scandolo8a902e12016-06-17 10:04:28 -0700351
352 it('should not render the filter field', () => {
353 // displayed value is different from model val, filter would not work
354 let filter = $(element).find('tbody tr td')[0];
355 expect($(filter)).not.toContainElement('input');
356 });
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700357 });
Matteo Scandoloea6ef002016-05-13 10:12:09 -0700358
359 describe('and is icon', () => {
360
361 beforeEach(() => {
362 scope.config = {
363 columns: [
364 {
365 label: 'Label 1',
366 prop: 'label-1',
367 type: 'icon',
368 formatter: item => {
369 switch (item['label-1']){
Matteo Scandoloa31617e2016-05-18 14:57:56 -0700370 case 1:
371 return 'ok';
372 case 2:
373 return 'remove';
374 case 3:
375 return 'plus'
Matteo Scandoloea6ef002016-05-13 10:12:09 -0700376 }
377 }
378 }
379 ]
380 };
381 scope.data = [
382 {
383 'label-1': 1
384 },
385 {
386 'label-1': 2
387 },
388 {
389 'label-1': 3
390 }
391 ];
392 compileElement();
393 });
394
395 it('should render a custom icon', () => {
396 let td1 = $(element).find('tbody tr:first-child td')[0];
397 let td2 = $(element).find('tbody tr:nth-child(2) td')[0];
398 let td3 = $(element).find('tbody tr:last-child td')[0];
399 expect($(td1).find('i')).toHaveClass('glyphicon-ok');
400 expect($(td2).find('i')).toHaveClass('glyphicon-remove');
401 expect($(td3).find('i')).toHaveClass('glyphicon-plus');
402 });
403 });
Matteo Scandolo250a29c2016-04-27 17:26:21 -0700404 });
405
Matteo Scandoloabb75622016-05-06 13:09:19 -0700406 describe('when a link property is provided', () => {
407 beforeEach(() => {
408 scope.data = [
409 {
410 id: 1}
411 ];
412 scope.config = {
413 columns: [
414 {
415 label: 'Id',
416 prop: 'id',
417 link: (item) => {
418 return `/link/${item.id}`;
419 }
420 }
421 ]
422 }
423 compileElement();
424 });
425
426 it('should check that the link property is a function', () => {
427 function errorFunctionWrapper(){
428 // setup the parent scope
429 scope = rootScope.$new();
430 scope.config = {
431 columns: [
432 {
433 label: 'Categories',
434 prop: 'categories',
435 link: 'custom'
436 }
437 ]
438 };
439 compileElement();
440 }
441 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] The link property should be a function.'));
442 });
443
Matteo Scandolob4fdd0a2016-05-09 15:27:47 -0700444 it('should render a link with the correct url', () => {
Matteo Scandoloabb75622016-05-06 13:09:19 -0700445 let link = $(element).find('tbody tr:first-child td a')[0];
446 expect($(link).attr('href')).toEqual('/link/1');
447 });
448 });
449
Matteo Scandolo6a6586e2016-04-14 16:52:13 -0700450 describe('when actions are passed', () => {
451
452 let cb = jasmine.createSpy('callback')
453
454 beforeEach(() => {
455 isolatedScope.config.actions = [
456 {
457 label: 'delete',
458 icon: 'remove',
459 cb: cb,
460 color: 'red'
461 }
462 ];
463 scope.$digest();
464 });
465
466 it('should have 3 columns', () => {
467 var th = element[0].getElementsByTagName('th');
468 expect(th.length).toEqual(3);
469 expect(isolatedScope.columns.length).toEqual(2);
470 });
471
472 it('when clicking on action should invoke callback', () => {
473 var link = element[0].getElementsByTagName('a')[0];
474 link.click();
475 expect(cb).toHaveBeenCalledWith(scope.data[0]);
476 });
477 });
478
479 describe('when filter is fulltext', () => {
480 beforeEach(() => {
481 isolatedScope.config.filter = 'fulltext';
482 scope.$digest();
483 });
484
485 it('should render a text field', () => {
486 var textField = element[0].getElementsByTagName('input');
487 expect(textField.length).toEqual(1);
488 });
489
490 describe('and a value is enterd', () => {
491 beforeEach(() => {
492 isolatedScope.query = '2.2';
493 scope.$digest();
494 });
495
496 it('should contain 2 rows', function() {
497 var tr = element[0].getElementsByTagName('tr');
498 expect(tr.length).toEqual(2);
499 });
500 });
501 });
502
503 describe('when filter is field', () => {
504 beforeEach(() => {
505 isolatedScope.config.filter = 'field';
506 scope.$digest();
507 });
508
509 it('should render a text field for each column', () => {
510 var textField = element[0].getElementsByTagName('input');
511 expect(textField.length).toEqual(2);
512 });
513
514 describe('and a value is enterd', () => {
515 beforeEach(() => {
516 isolatedScope.query = {'label-1': '2.1'};
517 scope.$digest();
518 });
519
520 it('should contain 3 rows', function() {
521 var tr = element[0].getElementsByTagName('tr');
522 expect(tr.length).toEqual(3);
523 });
524 });
525 });
Matteo Scandolof32a2e92016-04-14 17:19:08 -0700526
527 describe('when order is true', () => {
528 beforeEach(() => {
529 isolatedScope.config.order = true;
530 scope.$digest();
531 });
532
533 it('should render a arrows beside', () => {
534 var arrows = element[0].getElementsByTagName('i');
535 expect(arrows.length).toEqual(4);
536 });
537
Matteo Scandoloea6ef002016-05-13 10:12:09 -0700538 describe('and a default ordering is passed', () => {
539
540 beforeEach(() => {
541 scope.config.order = {
542 field: 'label-1',
543 reverse: true
544 };
545 compileElement();
546 });
547
548 it('should orderBy the default order', () => {
549 var tr = $(element).find('tr');
550 expect($(tr[1]).text()).toContain('Sample 2.2');
551 expect($(tr[2]).text()).toContain('Sample 1.1');
552 });
553 });
554
Matteo Scandolof32a2e92016-04-14 17:19:08 -0700555 describe('and an order is set', () => {
556 beforeEach(() => {
557 isolatedScope.orderBy = 'label-1';
558 isolatedScope.reverse = true;
559 scope.$digest();
560 });
561
562 it('should orderBy', function() {
Matteo Scandoloea6ef002016-05-13 10:12:09 -0700563 // console.log($(element).find('table'));
Matteo Scandoloabb75622016-05-06 13:09:19 -0700564 var tr = $(element).find('tr');
565 expect($(tr[1]).text()).toContain('Sample 2.2');
566 expect($(tr[2]).text()).toContain('Sample 1.1');
Matteo Scandolof32a2e92016-04-14 17:19:08 -0700567 });
568 });
569 });
Matteo Scandoloa7e64fd2016-04-14 14:59:09 -0700570 });
571 });
572 });
573})();
574