blob: a87e945532488f721ffdb5970b6a24206c11defa [file] [log] [blame]
Matteo Scandolo9e6c6fc2016-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 Scandolo18e55472016-07-06 11:18:58 -070010 var scope, element, isolatedScope, rootScope, compile, filter;
Matteo Scandolof9700a32016-05-06 09:42:45 -070011 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;
Matteo Scandolo18e55472016-07-06 11:18:58 -070021 };
Matteo Scandolof9700a32016-05-06 09:42:45 -070022
23
Matteo Scandolo9e6c6fc2016-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 Scandolo18e55472016-07-06 11:18:58 -070029 beforeEach(inject(function ($compile, $rootScope, $filter) {
Matteo Scandolof9700a32016-05-06 09:42:45 -070030 compile = $compile;
31 rootScope = $rootScope;
Matteo Scandolo18e55472016-07-06 11:18:58 -070032 filter = $filter;
Matteo Scandolof9700a32016-05-06 09:42:45 -070033 }));
34
Matteo Scandolob6ca3012016-06-03 16:58:43 -070035 it('should throw an error if no config is specified', () => {
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070036 function errorFunctionWrapper(){
Matteo Scandolof9700a32016-05-06 09:42:45 -070037 compileElement();
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070038 }
39 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a configuration via the "config" attribute'));
Matteo Scandolob6ca3012016-06-03 16:58:43 -070040 });
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070041
Matteo Scandolob6ca3012016-06-03 16:58:43 -070042 it('should throw an error if no config columns are specified', () => {
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070043 function errorFunctionWrapper(){
44 // setup the parent scope
Matteo Scandolob6ca3012016-06-03 16:58:43 -070045 scope = rootScope.$new();
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070046 scope.config = 'green';
Matteo Scandolof9700a32016-05-06 09:42:45 -070047 compileElement();
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070048 }
49 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a columns list in the configuration'));
Matteo Scandolob6ca3012016-06-03 16:58:43 -070050 });
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070051
Matteo Scandolo36639fc2016-06-15 13:40:17 -070052 describe('when basically configured', function() {
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070053
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070054 beforeEach(inject(function ($compile, $rootScope) {
Matteo Scandolof9700a32016-05-06 09:42:45 -070055
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070056 scope = $rootScope.$new();
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070057
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070058 scope.config = {
59 columns: [
60 {
61 label: 'Label 1',
62 prop: 'label-1'
63 },
64 {
65 label: 'Label 2',
66 prop: 'label-2'
67 }
68 ]
69 };
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070070
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070071 scope.data = [
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070072 {
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070073 'label-1': 'Sample 1.1',
74 'label-2': 'Sample 1.2'
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070075 },
76 {
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070077 'label-1': 'Sample 2.1',
78 'label-2': 'Sample 2.2'
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070079 }
80 ]
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070081
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070082 element = angular.element('<xos-table config="config" data="data"></xos-table>');
83 $compile(element)(scope);
84 scope.$digest();
85 isolatedScope = element.isolateScope().vm;
86 }));
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070087
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070088 it('should contain 2 columns', function() {
89 var th = element[0].getElementsByTagName('th');
90 expect(th.length).toEqual(2);
91 expect(isolatedScope.columns.length).toEqual(2);
92 });
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -070093
Matteo Scandoloa6a9e612016-04-14 16:52:13 -070094 it('should contain 3 rows', function() {
95 var tr = element[0].getElementsByTagName('tr');
96 expect(tr.length).toEqual(3);
97 });
98
Matteo Scandolof9700a32016-05-06 09:42:45 -070099 it('should render labels', () => {
100 let label1 = $(element).find('thead tr th')[0]
101 let label2 = $(element).find('thead tr th')[1]
102 expect($(label1).text().trim()).toEqual('Label 1');
103 expect($(label2).text().trim()).toEqual('Label 2');
104 });
105
Matteo Scandoloe15a8202016-04-15 14:27:54 -0700106 describe('when no data are provided', () => {
107 beforeEach(() => {
108 isolatedScope.data = [];
109 scope.$digest();
110 });
111 it('should render an alert', () => {
112 let alert = element[0].getElementsByClassName('alert');
113 let table = element[0].getElementsByTagName('table');
114 expect(alert.length).toEqual(1);
115 expect(table.length).toEqual(1);
116 });
117 });
118
Matteo Scandolof9700a32016-05-06 09:42:45 -0700119 describe('when a field type is provided', () => {
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700120 describe('and is boolean', () => {
121 beforeEach(() => {
Matteo Scandolof9700a32016-05-06 09:42:45 -0700122 scope.config = {
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700123 columns: [
124 {
125 label: 'Label 1',
126 prop: 'label-1',
127 type: 'boolean'
Matteo Scandolodd11d7c2016-06-14 17:26:51 -0700128 },
129 {
130 label: 'Label 2',
131 prop: 'label-2',
132 type: 'boolean'
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700133 }
134 ]
135 };
Matteo Scandolof9700a32016-05-06 09:42:45 -0700136 scope.data = [
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700137 {
Matteo Scandolodd11d7c2016-06-14 17:26:51 -0700138 'label-1': true,
139 'label-2': 1
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700140 },
141 {
Matteo Scandolodd11d7c2016-06-14 17:26:51 -0700142 'label-1': false,
143 'label-2': 0
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700144 }
145 ];
Matteo Scandolof9700a32016-05-06 09:42:45 -0700146 compileElement();
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700147 });
148
149 it('should render an incon', () => {
150 let td1 = $(element).find('tbody tr:first-child td')[0];
151 let td2 = $(element).find('tbody tr:last-child td')[0];
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700152 expect($(td1).find('i')).toHaveClass('glyphicon-ok');
153 expect($(td2).find('i')).toHaveClass('glyphicon-remove');
154 });
Matteo Scandoloc647c9b2016-05-18 14:57:56 -0700155
156 describe('with field filters', () => {
157 beforeEach(() => {
158 scope.config.filter = 'field';
159 compileElement();
160 });
161
162 it('should render a dropdown for filtering', () => {
163 let td1 = $(element).find('table tbody tr td')[0];
Matteo Scandoloc647c9b2016-05-18 14:57:56 -0700164 expect(td1).toContainElement('select');
165 expect(td1).not.toContainElement('input');
166 });
Matteo Scandolodd11d7c2016-06-14 17:26:51 -0700167
168 it('should correctly filter results', () => {
169 isolatedScope.query = {
170 'label-1': false
171 };
172 scope.$digest();
173 expect(isolatedScope.query['label-1']).toBeFalsy();
174 var tr = $(element).find('tbody:last-child > tr');
175 var icon = $(tr[0]).find('td i');
176 expect(tr.length).toEqual(1);
177 expect(icon).toHaveClass('glyphicon-remove');
178 });
179
180 it('should correctly filter results if the field is in the form of 0|1', () => {
181 isolatedScope.query = {
182 'label-2': false
183 };
184 scope.$digest();
185 expect(isolatedScope.query['label-1']).toBeFalsy();
186 var tr = $(element).find('tbody:last-child > tr');
187 var icon = $(tr[0]).find('td i');
188 expect(tr.length).toEqual(1);
189 expect(icon).toHaveClass('glyphicon-remove');
190 });
Matteo Scandoloc647c9b2016-05-18 14:57:56 -0700191 });
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700192 });
193
194 describe('and is date', () => {
195 beforeEach(() => {
Matteo Scandolof9700a32016-05-06 09:42:45 -0700196 scope.config = {
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700197 columns: [
198 {
199 label: 'Label 1',
200 prop: 'label-1',
201 type: 'date'
202 }
203 ]
204 };
Matteo Scandolof9700a32016-05-06 09:42:45 -0700205 scope.data = [
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700206 {
207 'label-1': '2015-02-17T22:06:38.059000Z'
208 }
209 ];
Matteo Scandolof9700a32016-05-06 09:42:45 -0700210 compileElement();
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700211 });
212
213 it('should render an formatted date', () => {
214 let td1 = $(element).find('tbody tr:first-child td')[0];
Matteo Scandolo18e55472016-07-06 11:18:58 -0700215 const expectedDate = filter('date')(scope.data[0]['label-1'], 'H:mm MMM d, yyyy');
216 expect($(td1).text().trim()).toEqual(expectedDate);
Matteo Scandolof9700a32016-05-06 09:42:45 -0700217 });
218 });
219
220 describe('and is array', () => {
221 beforeEach(() => {
222 scope.data = [
223 {categories: ['Film', 'Music']}
224 ];
225 scope.config = {
Matteo Scandolo36639fc2016-06-15 13:40:17 -0700226 filter: 'field',
Matteo Scandolof9700a32016-05-06 09:42:45 -0700227 columns: [
228 {
229 label: 'Categories',
230 prop: 'categories',
231 type: 'array'
232 }
233 ]
234 }
235 compileElement();
236 });
237 it('should render a comma separated list', () => {
Matteo Scandolo36639fc2016-06-15 13:40:17 -0700238 let td1 = $(element).find('tbody:last-child tr:first-child')[0];
Matteo Scandolof9700a32016-05-06 09:42:45 -0700239 expect($(td1).text().trim()).toEqual('Film, Music');
240 });
Matteo Scandolo36639fc2016-06-15 13:40:17 -0700241
242 it('should not render the filter field', () => {
243 let filter = $(element).find('tbody tr td')[0];
244 expect($(filter)).not.toContainElement('input');
245 });
Matteo Scandolof9700a32016-05-06 09:42:45 -0700246 });
247
Matteo Scandolo58705a42016-05-06 10:08:34 -0700248 describe('and is object', () => {
249 beforeEach(() => {
250 scope.data = [
251 {
252 attributes: {
253 age: 20,
254 height: 50
255 }
256 }
257 ];
258 scope.config = {
Matteo Scandolo36639fc2016-06-15 13:40:17 -0700259 filter: 'field',
Matteo Scandolo58705a42016-05-06 10:08:34 -0700260 columns: [
261 {
262 label: 'Categories',
263 prop: 'attributes',
264 type: 'object'
265 }
266 ]
267 }
268 compileElement();
269 });
270 it('should render a list of key-values', () => {
Matteo Scandolo36639fc2016-06-15 13:40:17 -0700271 let td = $(element).find('tbody:last-child tr:first-child')[0];
Matteo Scandolo0cd52c12016-05-06 11:39:56 -0700272 let ageLabel = $(td).find('dl dt')[0];
273 let ageValue = $(td).find('dl dd')[0];
274 let heightLabel = $(td).find('dl dt')[1];
275 let heightValue = $(td).find('dl dd')[1];
Matteo Scandolo58705a42016-05-06 10:08:34 -0700276 expect($(ageLabel).text().trim()).toEqual('age');
277 expect($(ageValue).text().trim()).toEqual('20');
278 expect($(heightLabel).text().trim()).toEqual('height');
279 expect($(heightValue).text().trim()).toEqual('50');
280 });
Matteo Scandolo36639fc2016-06-15 13:40:17 -0700281
282 it('should not render the filter field', () => {
283 let filter = $(element).find('tbody tr td')[0];
284 expect($(filter)).not.toContainElement('input');
285 });
Matteo Scandolo58705a42016-05-06 10:08:34 -0700286 });
287
Matteo Scandolof9700a32016-05-06 09:42:45 -0700288 describe('and is custom', () => {
Matteo Scandoloa7ad4992016-05-09 15:27:47 -0700289
290 let formatterFn = jasmine.createSpy('formatter').and.returnValue('Formatted Content');
291
Matteo Scandolof9700a32016-05-06 09:42:45 -0700292 beforeEach(() => {
293 scope.data = [
294 {categories: ['Film', 'Music']}
295 ];
296 scope.config = {
Matteo Scandolo5eac68a2016-06-17 10:04:28 -0700297 filter: 'field',
Matteo Scandolof9700a32016-05-06 09:42:45 -0700298 columns: [
299 {
300 label: 'Categories',
301 prop: 'categories',
302 type: 'custom',
Matteo Scandoloa7ad4992016-05-09 15:27:47 -0700303 formatter: formatterFn
Matteo Scandolof9700a32016-05-06 09:42:45 -0700304 }
305 ]
306 }
307 compileElement();
308 });
309
310 it('should check for a formatter property', () => {
311 function errorFunctionWrapper(){
312 // setup the parent scope
313 scope = rootScope.$new();
314 scope.config = {
315 columns: [
316 {
317 label: 'Categories',
318 prop: 'categories',
319 type: 'custom'
320 }
321 ]
322 };
323 compileElement();
324 }
325 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.'));
326 });
327
Matteo Scandolobee3eaf2016-05-06 13:09:19 -0700328 it('should check that the formatter property is a function', () => {
329 function errorFunctionWrapper(){
330 // setup the parent scope
331 scope = rootScope.$new();
332 scope.config = {
333 columns: [
334 {
335 label: 'Categories',
336 prop: 'categories',
337 type: 'custom',
338 formatter: 'formatter'
339 }
340 ]
341 };
342 compileElement();
343 }
344 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.'));
345 });
346
Matteo Scandolof9700a32016-05-06 09:42:45 -0700347 it('should format data using the formatter property', () => {
Matteo Scandolo5eac68a2016-06-17 10:04:28 -0700348 let td1 = $(element).find('tbody:last-child tr:first-child')[0];
Matteo Scandolof9700a32016-05-06 09:42:45 -0700349 expect($(td1).text().trim()).toEqual('Formatted Content');
Matteo Scandoloa7ad4992016-05-09 15:27:47 -0700350 // the custom formatted should receive the entire object, otherwise is not so custom
351 expect(formatterFn).toHaveBeenCalledWith({categories: ['Film', 'Music']});
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700352 });
Matteo Scandolo5eac68a2016-06-17 10:04:28 -0700353
354 it('should not render the filter field', () => {
355 // displayed value is different from model val, filter would not work
356 let filter = $(element).find('tbody tr td')[0];
357 expect($(filter)).not.toContainElement('input');
358 });
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700359 });
Matteo Scandolod49ed5f2016-05-13 10:12:09 -0700360
361 describe('and is icon', () => {
362
363 beforeEach(() => {
364 scope.config = {
365 columns: [
366 {
367 label: 'Label 1',
368 prop: 'label-1',
369 type: 'icon',
370 formatter: item => {
371 switch (item['label-1']){
Matteo Scandoloc647c9b2016-05-18 14:57:56 -0700372 case 1:
373 return 'ok';
374 case 2:
375 return 'remove';
376 case 3:
377 return 'plus'
Matteo Scandolod49ed5f2016-05-13 10:12:09 -0700378 }
379 }
380 }
381 ]
382 };
383 scope.data = [
384 {
385 'label-1': 1
386 },
387 {
388 'label-1': 2
389 },
390 {
391 'label-1': 3
392 }
393 ];
394 compileElement();
395 });
396
397 it('should render a custom icon', () => {
398 let td1 = $(element).find('tbody tr:first-child td')[0];
399 let td2 = $(element).find('tbody tr:nth-child(2) td')[0];
400 let td3 = $(element).find('tbody tr:last-child td')[0];
401 expect($(td1).find('i')).toHaveClass('glyphicon-ok');
402 expect($(td2).find('i')).toHaveClass('glyphicon-remove');
403 expect($(td3).find('i')).toHaveClass('glyphicon-plus');
404 });
405 });
Matteo Scandoloeabfb712016-04-27 17:26:21 -0700406 });
407
Matteo Scandolobee3eaf2016-05-06 13:09:19 -0700408 describe('when a link property is provided', () => {
409 beforeEach(() => {
410 scope.data = [
411 {
412 id: 1}
413 ];
414 scope.config = {
415 columns: [
416 {
417 label: 'Id',
418 prop: 'id',
419 link: (item) => {
420 return `/link/${item.id}`;
421 }
422 }
423 ]
424 }
425 compileElement();
426 });
427
428 it('should check that the link property is a function', () => {
429 function errorFunctionWrapper(){
430 // setup the parent scope
431 scope = rootScope.$new();
432 scope.config = {
433 columns: [
434 {
435 label: 'Categories',
436 prop: 'categories',
437 link: 'custom'
438 }
439 ]
440 };
441 compileElement();
442 }
443 expect(errorFunctionWrapper).toThrow(new Error('[xosTable] The link property should be a function.'));
444 });
445
Matteo Scandoloa7ad4992016-05-09 15:27:47 -0700446 it('should render a link with the correct url', () => {
Matteo Scandolobee3eaf2016-05-06 13:09:19 -0700447 let link = $(element).find('tbody tr:first-child td a')[0];
448 expect($(link).attr('href')).toEqual('/link/1');
449 });
450 });
451
Matteo Scandoloa6a9e612016-04-14 16:52:13 -0700452 describe('when actions are passed', () => {
453
454 let cb = jasmine.createSpy('callback')
455
456 beforeEach(() => {
457 isolatedScope.config.actions = [
458 {
459 label: 'delete',
460 icon: 'remove',
461 cb: cb,
462 color: 'red'
463 }
464 ];
465 scope.$digest();
466 });
467
468 it('should have 3 columns', () => {
469 var th = element[0].getElementsByTagName('th');
470 expect(th.length).toEqual(3);
471 expect(isolatedScope.columns.length).toEqual(2);
472 });
473
474 it('when clicking on action should invoke callback', () => {
475 var link = element[0].getElementsByTagName('a')[0];
476 link.click();
477 expect(cb).toHaveBeenCalledWith(scope.data[0]);
478 });
479 });
480
481 describe('when filter is fulltext', () => {
482 beforeEach(() => {
483 isolatedScope.config.filter = 'fulltext';
484 scope.$digest();
485 });
486
487 it('should render a text field', () => {
488 var textField = element[0].getElementsByTagName('input');
489 expect(textField.length).toEqual(1);
490 });
491
492 describe('and a value is enterd', () => {
493 beforeEach(() => {
494 isolatedScope.query = '2.2';
495 scope.$digest();
496 });
497
498 it('should contain 2 rows', function() {
499 var tr = element[0].getElementsByTagName('tr');
500 expect(tr.length).toEqual(2);
501 });
502 });
503 });
504
505 describe('when filter is field', () => {
506 beforeEach(() => {
507 isolatedScope.config.filter = 'field';
508 scope.$digest();
509 });
510
511 it('should render a text field for each column', () => {
512 var textField = element[0].getElementsByTagName('input');
513 expect(textField.length).toEqual(2);
514 });
515
516 describe('and a value is enterd', () => {
517 beforeEach(() => {
518 isolatedScope.query = {'label-1': '2.1'};
519 scope.$digest();
520 });
521
522 it('should contain 3 rows', function() {
523 var tr = element[0].getElementsByTagName('tr');
524 expect(tr.length).toEqual(3);
525 });
526 });
527 });
Matteo Scandolo03e59602016-04-14 17:19:08 -0700528
529 describe('when order is true', () => {
530 beforeEach(() => {
531 isolatedScope.config.order = true;
532 scope.$digest();
533 });
534
535 it('should render a arrows beside', () => {
536 var arrows = element[0].getElementsByTagName('i');
537 expect(arrows.length).toEqual(4);
538 });
539
Matteo Scandolod49ed5f2016-05-13 10:12:09 -0700540 describe('and a default ordering is passed', () => {
541
542 beforeEach(() => {
543 scope.config.order = {
544 field: 'label-1',
545 reverse: true
546 };
547 compileElement();
548 });
549
550 it('should orderBy the default order', () => {
551 var tr = $(element).find('tr');
552 expect($(tr[1]).text()).toContain('Sample 2.2');
553 expect($(tr[2]).text()).toContain('Sample 1.1');
554 });
555 });
556
Matteo Scandolo03e59602016-04-14 17:19:08 -0700557 describe('and an order is set', () => {
558 beforeEach(() => {
559 isolatedScope.orderBy = 'label-1';
560 isolatedScope.reverse = true;
561 scope.$digest();
562 });
563
564 it('should orderBy', function() {
Matteo Scandolod49ed5f2016-05-13 10:12:09 -0700565 // console.log($(element).find('table'));
Matteo Scandolobee3eaf2016-05-06 13:09:19 -0700566 var tr = $(element).find('tr');
567 expect($(tr[1]).text()).toContain('Sample 2.2');
568 expect($(tr[2]).text()).toContain('Sample 1.1');
Matteo Scandolo03e59602016-04-14 17:19:08 -0700569 });
570 });
571 });
Matteo Scandolo9e6c6fc2016-04-14 14:59:09 -0700572 });
573 });
574 });
575})();
576