]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts
import 15.2.4
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / shared / datatable / table / table.component.spec.ts
CommitLineData
11fdf7f2
TL
1import { ComponentFixture, TestBed } from '@angular/core/testing';
2import { FormsModule } from '@angular/forms';
e306af50 3import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
11fdf7f2
TL
4import { RouterTestingModule } from '@angular/router/testing';
5
6import { NgxDatatableModule } from '@swimlane/ngx-datatable';
7import * as _ from 'lodash';
9f95a23c 8import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
11fdf7f2
TL
9
10import { configureTestBed } from '../../../../testing/unit-test-helper';
11import { ComponentsModule } from '../../components/components.module';
9f95a23c 12import { CdTableColumnFilter } from '../../models/cd-table-column-filter';
11fdf7f2 13import { CdTableFetchDataContext } from '../../models/cd-table-fetch-data-context';
9f95a23c 14import { PipesModule } from '../../pipes/pipes.module';
11fdf7f2
TL
15import { TableComponent } from './table.component';
16
17describe('TableComponent', () => {
18 let component: TableComponent;
19 let fixture: ComponentFixture<TableComponent>;
20
9f95a23c 21 const createFakeData = (n: number) => {
11fdf7f2
TL
22 const data = [];
23 for (let i = 0; i < n; i++) {
24 data.push({
25 a: i,
9f95a23c
TL
26 b: i * 10,
27 c: !!(i % 2)
11fdf7f2
TL
28 });
29 }
30 return data;
31 };
32
33 const clearLocalStorage = () => {
34 component.localStorage.clear();
35 };
36
37 configureTestBed({
38 declarations: [TableComponent],
9f95a23c 39 imports: [
e306af50 40 BrowserAnimationsModule,
9f95a23c
TL
41 NgxDatatableModule,
42 FormsModule,
43 ComponentsModule,
44 RouterTestingModule,
45 BsDropdownModule.forRoot(),
46 PipesModule
47 ]
11fdf7f2
TL
48 });
49
50 beforeEach(() => {
51 fixture = TestBed.createComponent(TableComponent);
52 component = fixture.componentInstance;
11fdf7f2 53
9f95a23c 54 component.data = createFakeData(10);
11fdf7f2 55 component.columns = [
9f95a23c
TL
56 { prop: 'a', name: 'Index', filterable: true },
57 { prop: 'b', name: 'Index times ten' },
58 { prop: 'c', name: 'Odd?', filterable: true }
11fdf7f2
TL
59 ];
60 });
61
62 it('should create', () => {
63 expect(component).toBeTruthy();
64 });
65
9f95a23c
TL
66 it('should force an identifier', () => {
67 component.identifier = 'x';
68 component.forceIdentifier = true;
69 component.ngOnInit();
70 expect(component.identifier).toBe('x');
71 expect(component.sorts[0].prop).toBe('a');
72 expect(component.sorts).toEqual(component.createSortingDefinition('a'));
73 });
11fdf7f2 74
9f95a23c
TL
75 it('should have rows', () => {
76 component.useData();
77 expect(component.data.length).toBe(10);
78 expect(component.rows.length).toBe(component.data.length);
79 });
11fdf7f2 80
9f95a23c
TL
81 it('should have an int in setLimit parsing a string', () => {
82 expect(component.limit).toBe(10);
83 expect(component.limit).toEqual(jasmine.any(Number));
84
85 const e = { target: { value: '1' } };
86 component.setLimit(e);
87 expect(component.userConfig.limit).toBe(1);
88 expect(component.userConfig.limit).toEqual(jasmine.any(Number));
89 e.target.value = '-20';
90 component.setLimit(e);
91 expect(component.userConfig.limit).toBe(1);
92 });
11fdf7f2 93
9f95a23c
TL
94 it('should prevent propagation of mouseenter event', (done) => {
95 let wasCalled = false;
96 const mouseEvent = new MouseEvent('mouseenter');
97 mouseEvent.stopPropagation = () => {
98 wasCalled = true;
99 };
100 spyOn(component.table.element, 'addEventListener').and.callFake((eventName, fn) => {
101 fn(mouseEvent);
102 expect(eventName).toBe('mouseenter');
103 expect(wasCalled).toBe(true);
104 done();
11fdf7f2 105 });
9f95a23c
TL
106 component.ngOnInit();
107 });
11fdf7f2 108
9f95a23c
TL
109 describe('test column filtering', () => {
110 let filterIndex: CdTableColumnFilter;
111 let filterOdd: CdTableColumnFilter;
112 let filterCustom: CdTableColumnFilter;
113
114 const expectColumnFilterCreated = (
115 filter: CdTableColumnFilter,
116 prop: string,
117 options: string[],
118 value?: { raw: string; formatted: string }
119 ) => {
120 expect(filter.column.prop).toBe(prop);
121 expect(_.map(filter.options, 'raw')).toEqual(options);
122 expect(filter.value).toEqual(value);
123 };
124
125 const expectColumnFiltered = (
126 changes: { filter: CdTableColumnFilter; value?: string }[],
127 results: any[],
128 search: string = ''
129 ) => {
130 component.search = search;
131 _.forEach(changes, (change) => {
132 component.onChangeFilter(
133 change.filter,
134 change.value ? { raw: change.value, formatted: change.value } : undefined
135 );
494da23a 136 });
9f95a23c
TL
137 expect(component.rows).toEqual(results);
138 component.onClearSearch();
139 component.onClearFilters();
140 };
81eedcae 141
9f95a23c
TL
142 describe('with visible columns', () => {
143 beforeEach(() => {
144 component.initColumnFilters();
145 component.updateColumnFilterOptions();
146 filterIndex = component.columnFilters[0];
147 filterOdd = component.columnFilters[1];
148 });
11fdf7f2 149
9f95a23c
TL
150 it('should have filters initialized', () => {
151 expect(component.columnFilters.length).toBe(2);
152 expectColumnFilterCreated(
153 filterIndex,
154 'a',
155 _.map(component.data, (row) => _.toString(row.a))
156 );
157 expectColumnFilterCreated(filterOdd, 'c', ['false', 'true']);
11fdf7f2
TL
158 });
159
9f95a23c
TL
160 it('should add filters', () => {
161 // single
162 expectColumnFiltered([{ filter: filterIndex, value: '1' }], [{ a: 1, b: 10, c: true }]);
163
164 // multiple
165 expectColumnFiltered(
801d1391
TL
166 [
167 { filter: filterOdd, value: 'false' },
168 { filter: filterIndex, value: '2' }
169 ],
9f95a23c
TL
170 [{ a: 2, b: 20, c: false }]
171 );
172
173 // Clear should work
174 expect(component.rows).toEqual(component.data);
11fdf7f2
TL
175 });
176
9f95a23c
TL
177 it('should remove filters', () => {
178 // single
179 expectColumnFiltered(
180 [
181 { filter: filterOdd, value: 'true' },
182 { filter: filterIndex, value: '1' },
183 { filter: filterIndex, value: undefined }
184 ],
185 [
186 { a: 1, b: 10, c: true },
187 { a: 3, b: 30, c: true },
188 { a: 5, b: 50, c: true },
189 { a: 7, b: 70, c: true },
190 { a: 9, b: 90, c: true }
191 ]
192 );
193
194 // multiple
195 expectColumnFiltered(
196 [
197 { filter: filterOdd, value: 'true' },
198 { filter: filterIndex, value: '1' },
199 { filter: filterIndex, value: undefined },
200 { filter: filterOdd, value: undefined }
201 ],
202 component.data
203 );
204
205 // a selected filter should be removed if it's selected again
206 expectColumnFiltered(
207 [
208 { filter: filterOdd, value: 'true' },
209 { filter: filterIndex, value: '1' },
210 { filter: filterIndex, value: '1' }
211 ],
212 [
213 { a: 1, b: 10, c: true },
214 { a: 3, b: 30, c: true },
215 { a: 5, b: 50, c: true },
216 { a: 7, b: 70, c: true },
217 { a: 9, b: 90, c: true }
218 ]
219 );
11fdf7f2
TL
220 });
221
9f95a23c
TL
222 it('should search from filtered rows', () => {
223 expectColumnFiltered(
224 [{ filter: filterOdd, value: 'true' }],
225 [{ a: 9, b: 90, c: true }],
226 '9'
227 );
228
229 // Clear should work
230 expect(component.rows).toEqual(component.data);
11fdf7f2 231 });
9f95a23c 232 });
11fdf7f2 233
9f95a23c
TL
234 describe('with custom columns', () => {
235 beforeEach(() => {
236 // create a new additional column in data
237 for (let i = 0; i < component.data.length; i++) {
238 const row = component.data[i];
239 row['d'] = row.a;
240 }
241 // create a custom column filter
242 component.extraFilterableColumns = [
243 {
244 name: 'd less than 5',
245 prop: 'd',
246 filterOptions: ['yes', 'no'],
247 filterInitValue: 'yes',
248 filterPredicate: (row, value) => {
249 if (value === 'yes') {
250 return row.d < 5;
251 } else {
252 return row.d >= 5;
253 }
254 }
255 }
256 ];
257 component.initColumnFilters();
258 component.updateColumnFilterOptions();
259 filterIndex = component.columnFilters[0];
260 filterOdd = component.columnFilters[1];
261 filterCustom = component.columnFilters[2];
11fdf7f2
TL
262 });
263
9f95a23c
TL
264 it('should have filters initialized', () => {
265 expect(component.columnFilters.length).toBe(3);
266 expectColumnFilterCreated(filterCustom, 'd', ['yes', 'no'], {
267 raw: 'yes',
268 formatted: 'yes'
269 });
270 component.useData();
271 expect(component.rows).toEqual(_.slice(component.data, 0, 5));
11fdf7f2
TL
272 });
273
9f95a23c
TL
274 it('should remove filters', () => {
275 expectColumnFiltered([{ filter: filterCustom, value: 'no' }], _.slice(component.data, 5));
11fdf7f2 276 });
9f95a23c
TL
277 });
278 });
279
280 describe('test search', () => {
281 const expectSearch = (keyword: string, expectedResult: object[]) => {
282 component.search = keyword;
283 component.updateFilter();
284 expect(component.rows).toEqual(expectedResult);
285 component.onClearSearch();
286 };
287
288 describe('searchableObjects', () => {
289 const testObject = {
290 obj: {
291 min: 8,
292 max: 123
293 }
294 };
11fdf7f2 295
9f95a23c
TL
296 beforeEach(() => {
297 component.data = [testObject];
298 component.columns = [{ prop: 'obj', name: 'Object' }];
11fdf7f2
TL
299 });
300
9f95a23c
TL
301 it('should not search through objects as default case', () => {
302 expect(component.searchableObjects).toBe(false);
303 expectSearch('8', []);
11fdf7f2
TL
304 });
305
9f95a23c
TL
306 it('should search through objects if searchableObjects is set to true', () => {
307 component.searchableObjects = true;
308 expectSearch('28', []);
309 expectSearch('8', [testObject]);
310 expectSearch('123', [testObject]);
311 expectSearch('max', [testObject]);
11fdf7f2
TL
312 });
313 });
9f95a23c
TL
314
315 it('should find a particular number', () => {
316 expectSearch('5', [{ a: 5, b: 50, c: true }]);
317 expectSearch('9', [{ a: 9, b: 90, c: true }]);
318 });
319
320 it('should find boolean values', () => {
321 expectSearch('true', [
322 { a: 1, b: 10, c: true },
323 { a: 3, b: 30, c: true },
324 { a: 5, b: 50, c: true },
325 { a: 7, b: 70, c: true },
326 { a: 9, b: 90, c: true }
327 ]);
328 expectSearch('false', [
329 { a: 0, b: 0, c: false },
330 { a: 2, b: 20, c: false },
331 { a: 4, b: 40, c: false },
332 { a: 6, b: 60, c: false },
333 { a: 8, b: 80, c: false }
334 ]);
335 });
336
337 it('should test search keyword preparation', () => {
338 const prepare = TableComponent.prepareSearch;
339 const expected = ['a', 'b', 'c'];
340 expect(prepare('a b c')).toEqual(expected);
341 expect(prepare('a,, b,, c')).toEqual(expected);
342 expect(prepare('a,,,, b,,, c')).toEqual(expected);
343 expect(prepare('a+b c')).toEqual(['a+b', 'c']);
344 expect(prepare('a,,,+++b,,, c')).toEqual(['a+++b', 'c']);
345 expect(prepare('"a b c" "d e f", "g, h i"')).toEqual(['a+b+c', 'd+e++f', 'g+h+i']);
346 });
347
348 it('should search for multiple values', () => {
349 expectSearch('2 20 false', [{ a: 2, b: 20, c: false }]);
350 expectSearch('false 2', [{ a: 2, b: 20, c: false }]);
351 });
352
353 it('should filter by column', () => {
354 expectSearch('index:5', [{ a: 5, b: 50, c: true }]);
355 expectSearch('times:50', [{ a: 5, b: 50, c: true }]);
356 expectSearch('times:50 index:5', [{ a: 5, b: 50, c: true }]);
357 expectSearch('Odd?:true', [
358 { a: 1, b: 10, c: true },
359 { a: 3, b: 30, c: true },
360 { a: 5, b: 50, c: true },
361 { a: 7, b: 70, c: true },
362 { a: 9, b: 90, c: true }
363 ]);
364 component.data = createFakeData(100);
365 expectSearch('index:1 odd:true times:110', [{ a: 11, b: 110, c: true }]);
366 });
367
368 it('should search through arrays', () => {
801d1391
TL
369 component.columns = [
370 { prop: 'a', name: 'Index' },
371 { prop: 'b', name: 'ArrayColumn' }
372 ];
373
374 component.data = [
375 { a: 1, b: ['foo', 'bar'] },
376 { a: 2, b: ['baz', 'bazinga'] }
377 ];
9f95a23c
TL
378 expectSearch('bar', [{ a: 1, b: ['foo', 'bar'] }]);
379 expectSearch('arraycolumn:bar arraycolumn:foo', [{ a: 1, b: ['foo', 'bar'] }]);
380 expectSearch('arraycolumn:baz arraycolumn:inga', [{ a: 2, b: ['baz', 'bazinga'] }]);
381
801d1391
TL
382 component.data = [
383 { a: 1, b: [1, 2] },
384 { a: 2, b: [3, 4] }
385 ];
9f95a23c
TL
386 expectSearch('arraycolumn:1 arraycolumn:2', [{ a: 1, b: [1, 2] }]);
387 });
388
389 it('should search with spaces', () => {
390 const expectedResult = [{ a: 2, b: 20, c: false }];
391 expectSearch(`'Index times ten':20`, expectedResult);
392 expectSearch('index+times+ten:20', expectedResult);
393 });
394
395 it('should filter results although column name is incomplete', () => {
396 component.data = createFakeData(3);
397 expectSearch(`'Index times ten'`, []);
398 expectSearch(`'Ind'`, []);
399 expectSearch(`'Ind:'`, [
400 { a: 0, b: 0, c: false },
401 { a: 1, b: 10, c: true },
402 { a: 2, b: 20, c: false }
403 ]);
404 });
405
406 it('should search if column name is incomplete', () => {
407 const expectedData = [
408 { a: 0, b: 0, c: false },
409 { a: 1, b: 10, c: true },
410 { a: 2, b: 20, c: false }
411 ];
412 component.data = _.clone(expectedData);
413 expectSearch('inde', []);
414 expectSearch('index:', expectedData);
415 expectSearch('index times te', []);
416 });
417
418 it('should restore full table after search', () => {
419 component.useData();
420 expect(component.rows.length).toBe(10);
421 component.search = '3';
422 component.updateFilter();
423 expect(component.rows.length).toBe(1);
424 component.onClearSearch();
425 expect(component.rows.length).toBe(10);
426 });
e306af50
TL
427
428 it('should work with undefined data', () => {
429 component.data = undefined;
430 component.search = '3';
431 component.updateFilter();
432 expect(component.rows).toBeUndefined();
433 });
11fdf7f2
TL
434 });
435
436 describe('after ngInit', () => {
9f95a23c 437 const toggleColumn = (prop: string, checked: boolean) => {
11fdf7f2
TL
438 component.toggleColumn({
439 target: {
440 name: prop,
441 checked: checked
442 }
443 });
444 };
445
446 const equalStorageConfig = () => {
447 expect(JSON.stringify(component.userConfig)).toBe(
448 component.localStorage.getItem(component.tableName)
449 );
450 };
451
452 beforeEach(() => {
453 component.ngOnInit();
454 });
455
456 it('should have updated the column definitions', () => {
457 expect(component.columns[0].flexGrow).toBe(1);
458 expect(component.columns[1].flexGrow).toBe(2);
459 expect(component.columns[2].flexGrow).toBe(2);
460 expect(component.columns[2].resizeable).toBe(false);
461 });
462
463 it('should have table columns', () => {
9f95a23c 464 expect(component.tableColumns.length).toBe(3);
11fdf7f2
TL
465 expect(component.tableColumns).toEqual(component.columns);
466 });
467
468 it('should have a unique identifier which it searches for', () => {
469 expect(component.identifier).toBe('a');
470 expect(component.userConfig.sorts[0].prop).toBe('a');
471 expect(component.userConfig.sorts).toEqual(component.createSortingDefinition('a'));
472 equalStorageConfig();
473 });
474
475 it('should remove column "a"', () => {
476 expect(component.userConfig.sorts[0].prop).toBe('a');
477 toggleColumn('a', false);
478 expect(component.userConfig.sorts[0].prop).toBe('b');
9f95a23c 479 expect(component.tableColumns.length).toBe(2);
11fdf7f2
TL
480 equalStorageConfig();
481 });
482
483 it('should not be able to remove all columns', () => {
484 expect(component.userConfig.sorts[0].prop).toBe('a');
485 toggleColumn('a', false);
486 toggleColumn('b', false);
487 toggleColumn('c', false);
9f95a23c 488 expect(component.userConfig.sorts[0].prop).toBe('c');
11fdf7f2
TL
489 expect(component.tableColumns.length).toBe(1);
490 equalStorageConfig();
491 });
492
493 it('should enable column "a" again', () => {
494 expect(component.userConfig.sorts[0].prop).toBe('a');
495 toggleColumn('a', false);
496 toggleColumn('a', true);
497 expect(component.userConfig.sorts[0].prop).toBe('b');
9f95a23c 498 expect(component.tableColumns.length).toBe(3);
11fdf7f2
TL
499 equalStorageConfig();
500 });
501
502 afterEach(() => {
503 clearLocalStorage();
504 });
505 });
506
507 describe('reload data', () => {
508 beforeEach(() => {
509 component.ngOnInit();
510 component.data = [];
511 component['updating'] = false;
512 });
513
514 it('should call fetchData callback function', () => {
9f95a23c 515 component.fetchData.subscribe((context: any) => {
11fdf7f2
TL
516 expect(context instanceof CdTableFetchDataContext).toBeTruthy();
517 });
518 component.reloadData();
519 });
520
521 it('should call error function', () => {
522 component.data = createFakeData(5);
9f95a23c 523 component.fetchData.subscribe((context: any) => {
11fdf7f2
TL
524 context.error();
525 expect(component.loadingError).toBeTruthy();
526 expect(component.data.length).toBe(0);
527 expect(component.loadingIndicator).toBeFalsy();
528 expect(component['updating']).toBeFalsy();
529 });
530 component.reloadData();
531 });
532
533 it('should call error function with custom config', () => {
534 component.data = createFakeData(10);
9f95a23c 535 component.fetchData.subscribe((context: any) => {
11fdf7f2
TL
536 context.errorConfig.resetData = false;
537 context.errorConfig.displayError = false;
538 context.error();
539 expect(component.loadingError).toBeFalsy();
540 expect(component.data.length).toBe(10);
541 expect(component.loadingIndicator).toBeFalsy();
542 expect(component['updating']).toBeFalsy();
543 });
544 component.reloadData();
545 });
546
547 it('should update selection on refresh - "onChange"', () => {
548 spyOn(component, 'onSelect').and.callThrough();
549 component.data = createFakeData(10);
550 component.selection.selected = [_.clone(component.data[1])];
551 component.updateSelectionOnRefresh = 'onChange';
552 component.updateSelected();
553 expect(component.onSelect).toHaveBeenCalledTimes(0);
554 component.data[1].d = !component.data[1].d;
555 component.updateSelected();
556 expect(component.onSelect).toHaveBeenCalled();
557 });
558
559 it('should update selection on refresh - "always"', () => {
560 spyOn(component, 'onSelect').and.callThrough();
561 component.data = createFakeData(10);
562 component.selection.selected = [_.clone(component.data[1])];
563 component.updateSelectionOnRefresh = 'always';
564 component.updateSelected();
565 expect(component.onSelect).toHaveBeenCalled();
566 component.data[1].d = !component.data[1].d;
567 component.updateSelected();
568 expect(component.onSelect).toHaveBeenCalled();
569 });
570
571 it('should update selection on refresh - "never"', () => {
572 spyOn(component, 'onSelect').and.callThrough();
573 component.data = createFakeData(10);
574 component.selection.selected = [_.clone(component.data[1])];
575 component.updateSelectionOnRefresh = 'never';
576 component.updateSelected();
577 expect(component.onSelect).toHaveBeenCalledTimes(0);
578 component.data[1].d = !component.data[1].d;
579 component.updateSelected();
580 expect(component.onSelect).toHaveBeenCalledTimes(0);
581 });
582
583 afterEach(() => {
584 clearLocalStorage();
585 });
586 });
587
588 describe('useCustomClass', () => {
589 beforeEach(() => {
590 component.customCss = {
9f95a23c 591 'badge badge-danger': 'active',
11fdf7f2 592 'secret secret-number': 123.456,
9f95a23c 593 btn: (v) => _.isString(v) && v.startsWith('http'),
11fdf7f2
TL
594 secure: (v) => _.isString(v) && v.startsWith('https')
595 };
596 });
597
598 it('should throw an error if custom classes are not set', () => {
599 component.customCss = undefined;
600 expect(() => component.useCustomClass('active')).toThrowError('Custom classes are not set!');
601 });
602
603 it('should not return any class', () => {
604 ['', 'something', 123, { complex: 1 }, [1, 2, 3]].forEach((value) =>
605 expect(component.useCustomClass(value)).toBe(undefined)
606 );
607 });
608
609 it('should match a string and return the corresponding class', () => {
9f95a23c 610 expect(component.useCustomClass('active')).toBe('badge badge-danger');
11fdf7f2
TL
611 });
612
613 it('should match a number and return the corresponding class', () => {
614 expect(component.useCustomClass(123.456)).toBe('secret secret-number');
615 });
616
617 it('should match against a function and return the corresponding class', () => {
9f95a23c 618 expect(component.useCustomClass('http://no.ssl')).toBe('btn');
11fdf7f2
TL
619 });
620
621 it('should match against multiple functions and return the corresponding classes', () => {
9f95a23c 622 expect(component.useCustomClass('https://secure.it')).toBe('btn secure');
11fdf7f2
TL
623 });
624 });
e306af50
TL
625
626 describe('test expand and collapse feature', () => {
627 beforeEach(() => {
628 spyOn(component.setExpandedRow, 'emit');
629 component.table = {
630 rowDetail: { collapseAllRows: jest.fn(), toggleExpandRow: jest.fn() }
631 } as any;
632
633 // Setup table
634 component.identifier = 'a';
635 component.data = createFakeData(10);
636
637 // Select item
638 component.expanded = _.clone(component.data[1]);
639 });
640
641 describe('update expanded on refresh', () => {
642 const updateExpendedOnState = (state: 'always' | 'never' | 'onChange') => {
643 component.updateExpandedOnRefresh = state;
644 component.updateExpanded();
645 };
646
647 beforeEach(() => {
648 // Mock change
649 component.data[1].b = 'test';
650 });
651
652 it('refreshes "always"', () => {
653 updateExpendedOnState('always');
654 expect(component.expanded.b).toBe('test');
655 expect(component.setExpandedRow.emit).toHaveBeenCalled();
656 });
657
658 it('refreshes "onChange"', () => {
659 updateExpendedOnState('onChange');
660 expect(component.expanded.b).toBe('test');
661 expect(component.setExpandedRow.emit).toHaveBeenCalled();
662 });
663
664 it('does not refresh "onChange" if data is equal', () => {
665 component.data[1].b = 10; // Reverts change
666 updateExpendedOnState('onChange');
667 expect(component.expanded.b).toBe(10);
668 expect(component.setExpandedRow.emit).not.toHaveBeenCalled();
669 });
670
671 it('"never" refreshes', () => {
672 updateExpendedOnState('never');
673 expect(component.expanded.b).toBe(10);
674 expect(component.setExpandedRow.emit).not.toHaveBeenCalled();
675 });
676 });
677
678 it('should open the table details and close other expanded rows', () => {
679 component.toggleExpandRow(component.expanded, false);
680 expect(component.expanded).toEqual({ a: 1, b: 10, c: true });
681 expect(component.table.rowDetail.collapseAllRows).toHaveBeenCalled();
682 expect(component.setExpandedRow.emit).toHaveBeenCalledWith(component.expanded);
683 expect(component.table.rowDetail.toggleExpandRow).toHaveBeenCalled();
684 });
685
686 it('should close the current table details expansion', () => {
687 component.toggleExpandRow(component.expanded, true);
688 expect(component.expanded).toBeUndefined();
689 expect(component.setExpandedRow.emit).toHaveBeenCalledWith(undefined);
690 expect(component.table.rowDetail.toggleExpandRow).toHaveBeenCalled();
691 });
692 });
11fdf7f2 693});