igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
749 lines (726 loc) • 40.2 kB
text/typescript
import { NgFor, NgIf } from '@angular/common';
import { ChangeDetectorRef, Component, DebugElement, ElementRef, EventEmitter, QueryList, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { DisplayDensity } from '../core/density';
import { AnimationService } from '../services/animation/animation';
import { configureTestSuite } from '../test-utils/configure-suite';
import { TreeTestFunctions } from './tree-functions.spec';
import { IgxTreeNavigationService } from './tree-navigation.service';
import { IgxTreeNodeComponent } from './tree-node/tree-node.component';
import { IgxTreeSelectionService } from './tree-selection.service';
import { IgxTreeComponent } from './tree.component';
import { IgxTreeService } from './tree.service';
const TREE_ROOT_CLASS = 'igx-tree__root';
const NODE_TAG = 'igx-tree-node';
describe('IgxTree #treeView', () => {
configureTestSuite();
describe('Unit Tests', () => {
let mockNavService: IgxTreeNavigationService;
let mockTreeService: IgxTreeService;
let mockSelectionService: IgxTreeSelectionService;
let mockElementRef: ElementRef<any>;
let mockNodes: QueryList<IgxTreeNodeComponent<any>>;
let mockNodesArray: IgxTreeNodeComponent<any>[] = [];
let tree: IgxTreeComponent = null;
beforeEach(() => {
mockNodesArray = [];
mockNavService = jasmine.createSpyObj('navService',
['register', 'update_disabled_cache', 'update_visible_cache',
'init_invisible_cache', 'setFocusedAndActiveNode', 'handleKeydown']);
mockTreeService = jasmine.createSpyObj('treeService',
['register', 'collapse', 'expand', 'collapsing', 'isExpanded']);
mockSelectionService = jasmine.createSpyObj('selectionService',
['register', 'deselectNodesWithNoEvent', 'ensureStateOnNodeDelete', 'selectNodesWithNoEvent']);
mockElementRef = jasmine.createSpyObj('elementRef', [], {
nativeElement: jasmine.createSpyObj('nativeElement', ['focus'], {})
});
tree?.ngOnDestroy();
tree = new IgxTreeComponent(mockNavService, mockSelectionService, mockTreeService, mockElementRef);
mockNodes = jasmine.createSpyObj('mockList', ['toArray'], {
changes: new Subject<void>(),
get first() {
return mockNodesArray[0];
},
get last() {
return mockNodesArray[mockNodesArray.length - 1];
},
get length() {
return mockNodesArray.length;
},
forEach: (cb: (n: IgxTreeNodeComponent<any>) => void): void => {
mockNodesArray.forEach(cb);
},
find: (cb: (n: IgxTreeNodeComponent<any>) => boolean): IgxTreeNodeComponent<any> => mockNodesArray.find(cb),
filter: jasmine.createSpy('filter').
and.callFake((cb: (n: IgxTreeNodeComponent<any>) => boolean): IgxTreeNodeComponent<any>[] => mockNodesArray.filter(cb)),
});
spyOn(mockNodes, 'toArray').and.returnValue(mockNodesArray);
});
afterEach(() => {
tree?.ngOnDestroy();
});
describe('IgxTreeComponent', () => {
it('Should update nav children cache when events are fired', fakeAsync(() => {
expect(mockNavService.init_invisible_cache).toHaveBeenCalledTimes(0);
expect(mockNavService.update_visible_cache).toHaveBeenCalledTimes(0);
expect(mockNavService.update_disabled_cache).toHaveBeenCalledTimes(0);
tree.ngOnInit();
tick();
expect(mockNavService.init_invisible_cache).toHaveBeenCalledTimes(0);
expect(mockNavService.update_visible_cache).toHaveBeenCalledTimes(0);
expect(mockNavService.update_disabled_cache).toHaveBeenCalledTimes(0);
tree.disabledChange.emit('mockNode' as any);
tick();
expect(mockNavService.update_disabled_cache).toHaveBeenCalledTimes(1);
expect(mockNavService.update_disabled_cache).toHaveBeenCalledWith('mockNode' as any);
tree.nodeCollapsing.emit({ node: 'mockNode' as any } as any);
tick();
expect(mockNavService.update_visible_cache).toHaveBeenCalledTimes(1);
expect(mockNavService.update_visible_cache).toHaveBeenCalledWith('mockNode' as any, false);
tree.nodeExpanding.emit({ node: 'mockNode' as any } as any);
tick();
expect(mockNavService.update_visible_cache).toHaveBeenCalledTimes(2);
expect(mockNavService.update_visible_cache).toHaveBeenCalledWith('mockNode' as any, true);
tree.nodes = mockNodes;
const mockNode = TreeTestFunctions.createNodeSpy({
expandedChange: new EventEmitter<void>(),
closeAnimationDone: new EventEmitter<void>(),
openAnimationDone: new EventEmitter<void>()
}) as any;
mockNodesArray.push(
mockNode
);
spyOnProperty(mockNodes, 'first', 'get').and.returnValue(mockNode);
tree.ngAfterViewInit();
tick();
expect(mockNavService.init_invisible_cache).toHaveBeenCalledTimes(1);
tree.nodes.first.expandedChange.emit(true);
expect(mockNavService.update_visible_cache).toHaveBeenCalledTimes(3);
expect(mockNavService.update_visible_cache).toHaveBeenCalledWith(tree.nodes.first, true);
tree.nodes.first.expandedChange.emit(false);
expect(mockNavService.update_visible_cache).toHaveBeenCalledTimes(4);
expect(mockNavService.update_visible_cache).toHaveBeenCalledWith(tree.nodes.first, false);
(tree.nodes.changes as any).next();
tick();
expect(mockNavService.init_invisible_cache).toHaveBeenCalledTimes(2);
tree.ngOnDestroy();
}));
it('Should update delegate keyboard events to nav service', () => {
const mockEvent: any = {};
tree.handleKeydown(mockEvent as any);
expect(mockNavService.handleKeydown).toHaveBeenCalledWith(mockEvent as any);
});
it('Should search through nodes and return expected value w/ `findNodes`', () => {
tree.nodes = mockNodes;
let id = 0;
let itemRef = {} as any;
mockNodesArray = TreeTestFunctions.createNodeSpies(0, 5);
mockNodesArray.forEach(n => {
itemRef = { id: id++ };
n.data = itemRef;
});
expect(tree.findNodes(itemRef)).toEqual([mockNodesArray[mockNodesArray.length - 1]]);
expect(tree.nodes.filter).toHaveBeenCalledTimes(1);
expect(tree.findNodes(1, (p, n) => n.data.id === p)).toEqual([mockNodes.find(n => n.data.id === 1)]);
expect(tree.nodes.filter).toHaveBeenCalledTimes(2);
expect(tree.findNodes('Not found', (p, n) => n.data.id === p)).toEqual(null);
expect(tree.nodes.filter).toHaveBeenCalledTimes(3);
});
it('Should return only root level nodes w/ `rootNodes` accessor', () => {
tree.nodes = mockNodes;
const arr = [];
for (let i = 0; i < 7; i++) {
const level = i > 4 ? 1 : 0;
arr.push({
level
});
}
mockNodesArray = [...arr];
expect(tree.rootNodes.length).toBe(5);
mockNodesArray.forEach(n => {
(n as any).level = 1;
});
expect(tree.rootNodes.length).toBe(0);
mockNodesArray.forEach(n => {
(n as any).level = 0;
});
expect(tree.rootNodes.length).toBe(7);
tree.nodes = null;
expect(tree.rootNodes).toBe(undefined);
});
it('Should expandAll nodes nodes w/ proper methods', () => {
tree.nodes = mockNodes;
const customArrayParam = [];
for (let i = 0; i < 5; i++) {
const node = jasmine.createSpyObj('node', ['expand', 'collapse'], {
_expanded: false,
get expanded() {
return this._expanded;
},
set expanded(val: boolean) {
this._expanded = val;
}
});
node.spyProp = spyOnProperty(node, 'expanded', 'set').and.callThrough();
mockNodesArray.push(node);
if (i > 3) {
customArrayParam.push(node);
}
}
spyOn(mockNodesArray, 'forEach').and.callThrough();
tree.expandAll();
expect(mockNodesArray.forEach).toHaveBeenCalledTimes(1);
mockNodesArray.forEach(n => {
expect((n as any).spyProp).toHaveBeenCalledWith(true);
expect((n as any).spyProp).toHaveBeenCalledTimes(1);
});
tree.expandAll(customArrayParam);
customArrayParam.forEach(n => {
expect((n as any).spyProp).toHaveBeenCalledWith(true);
expect((n as any).spyProp).toHaveBeenCalledTimes(2);
});
});
it('Should collapseAll nodes nodes w/ proper methods', () => {
tree.nodes = mockNodes;
const customArrayParam = [];
for (let i = 0; i < 5; i++) {
const node = jasmine.createSpyObj('node', ['expand', 'collapse'], {
_expanded: false,
get expanded() {
return this._expanded;
},
set expanded(val: boolean) {
this._expanded = val;
}
});
node.spyProp = spyOnProperty(node, 'expanded', 'set').and.callThrough();
mockNodesArray.push(node);
if (i > 3) {
customArrayParam.push(node);
}
}
spyOn(mockNodesArray, 'forEach').and.callThrough();
tree.collapseAll();
expect(mockNodesArray.forEach).toHaveBeenCalledTimes(1);
mockNodesArray.forEach(n => {
expect((n as any).spyProp).toHaveBeenCalledWith(false);
expect((n as any).spyProp).toHaveBeenCalledTimes(1);
});
tree.collapseAll(customArrayParam);
customArrayParam.forEach(n => {
expect((n as any).spyProp).toHaveBeenCalledWith(false);
expect((n as any).spyProp).toHaveBeenCalledTimes(2);
});
});
it('Should deselectAll nodes w/ proper methond', () => {
tree.nodes = mockNodes;
tree.deselectAll();
expect(mockSelectionService.deselectNodesWithNoEvent).toHaveBeenCalledWith(undefined);
const customParam = jasmine.createSpyObj<any>('nodes', ['toArray']);
tree.deselectAll(customParam);
expect(mockSelectionService.deselectNodesWithNoEvent).toHaveBeenCalledWith(customParam);
});
});
describe('IgxTreeNodeComponent', () => {
let mockTree: IgxTreeComponent;
let mockCdr: ChangeDetectorRef;
let mockAnimationService: AnimationService;
beforeEach(() => {
mockTree = jasmine.createSpyObj<any>('mockTree', ['findNodes'],
{
nodeCollapsing: jasmine.createSpyObj('spy', ['emit']),
nodeExpanding: jasmine.createSpyObj('spy', ['emit']),
nodeCollapsed: jasmine.createSpyObj('spy', ['emit']),
nodeExpanded: jasmine.createSpyObj('spy', ['emit']),
_displayDensity: DisplayDensity.comfortable,
get displayDensity() {
return this._displayDensity;
}
});
mockCdr = jasmine.createSpyObj<ChangeDetectorRef>('mockCdr', ['detectChanges', 'markForCheck'], {});
mockAnimationService = jasmine.createSpyObj<AnimationService>('mockAB', ['buildAnimation'], {});
});
it('Should call service expand/collapse methods when toggling state through `[expanded]` input', () => {
const node = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, null);
expect(mockTreeService.collapse).not.toHaveBeenCalled();
expect(mockTreeService.expand).not.toHaveBeenCalled();
expect(mockTree.nodeExpanded.emit).not.toHaveBeenCalled();
expect(mockTree.nodeCollapsed.emit).not.toHaveBeenCalled();
expect(mockTree.nodeExpanding.emit).not.toHaveBeenCalled();
expect(mockTree.nodeExpanded.emit).not.toHaveBeenCalled();
node.expanded = true;
expect(mockTreeService.expand).toHaveBeenCalledTimes(1);
expect(mockTreeService.expand).toHaveBeenCalledWith(node, false);
node.expanded = false;
expect(mockTreeService.collapse).toHaveBeenCalledTimes(1);
expect(mockTreeService.collapse).toHaveBeenCalledWith(node);
// events are not emitted when chainging state through input
expect(mockTree.nodeExpanded.emit).not.toHaveBeenCalled();
expect(mockTree.nodeCollapsed.emit).not.toHaveBeenCalled();
expect(mockTree.nodeExpanding.emit).not.toHaveBeenCalled();
expect(mockTree.nodeExpanded.emit).not.toHaveBeenCalled();
});
it('Expand() should expand currently collapsing node', () => {
mockTreeService = new IgxTreeService();
mockTreeService.register(mockTree);
const node = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, null);
mockTreeService.expandedNodes.add(node);
mockTreeService.collapsingNodes.add(node);
node.expand();
expect(mockTree.nodeExpanding.emit).toHaveBeenCalledTimes(1);
});
it('Collapse() shouldn`t affect a currently collapsing node', () => {
mockTreeService = new IgxTreeService();
mockTreeService.register(mockTree);
const node = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, null);
mockTreeService.expandedNodes.add(node);
mockTreeService.collapsingNodes.add(node);
node.collapse();
expect(mockTree.nodeCollapsing.emit).toHaveBeenCalledTimes(0);
});
it('Should call service expand/collapse methods when calling API state methods', () => {
mockTreeService = new IgxTreeService();
mockTreeService.register(mockTree);
const node = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, null);
node.expandedChange = jasmine.createSpyObj('emitter', ['emit'])
const openAnimationSpy = spyOn(node, 'playOpenAnimation');
const closeAnimationSpy = spyOn(node, 'playCloseAnimation');
const mockObj = jasmine.createSpyObj<any>('mockElement', ['focus']);
spyOn(mockTreeService, 'collapse').and.callThrough();
spyOn(mockTreeService, 'collapsing').and.callThrough();
spyOn(mockTreeService, 'expand').and.callThrough();
spyOn(node, 'expandedChange').and.callThrough();
const ingArgs = {
owner: mockTree,
cancel: false,
node
};
const edArgs = {
owner: mockTree,
node
};
(node as any).childrenContainer = mockObj;
expect(mockTreeService.collapse).not.toHaveBeenCalled();
expect(mockTreeService.expand).not.toHaveBeenCalled();
expect(mockTreeService.collapsing).not.toHaveBeenCalled();
expect(openAnimationSpy).not.toHaveBeenCalled();
expect(closeAnimationSpy).not.toHaveBeenCalled();
expect(mockCdr.markForCheck).not.toHaveBeenCalled();
expect(mockTreeService.collapsing).not.toHaveBeenCalled();
expect(mockTree.nodeExpanding.emit).not.toHaveBeenCalledWith();
expect(mockTree.nodeCollapsing.emit).not.toHaveBeenCalledWith();
expect(mockTree.nodeExpanded.emit).not.toHaveBeenCalledWith();
expect(mockTree.nodeCollapsed.emit).not.toHaveBeenCalledWith();
expect(node.expandedChange).not.toHaveBeenCalled();
node.ngOnInit();
node.expand();
expect(openAnimationSpy).toHaveBeenCalledWith(mockObj);
expect(openAnimationSpy).toHaveBeenCalledTimes(1);
expect(mockTree.nodeExpanded.emit).toHaveBeenCalledTimes(0);
expect(mockTree.nodeExpanding.emit).toHaveBeenCalledWith(ingArgs);
expect(mockTreeService.expand).toHaveBeenCalledWith(node, true);
expect(mockTreeService.expand).toHaveBeenCalledTimes(1);
node.openAnimationDone.emit();
expect(node.expandedChange.emit).toHaveBeenCalledTimes(1);
expect(node.expandedChange.emit).toHaveBeenCalledWith(true);
expect(mockTree.nodeExpanded.emit).toHaveBeenCalledTimes(1);
expect(mockTree.nodeExpanded.emit).toHaveBeenCalledWith(edArgs);
node.collapse();
expect(closeAnimationSpy).toHaveBeenCalledWith(mockObj);
expect(closeAnimationSpy).toHaveBeenCalledTimes(1);
expect(mockTree.nodeCollapsed.emit).toHaveBeenCalledTimes(0);
expect(mockTree.nodeCollapsing.emit).toHaveBeenCalledWith(ingArgs);
// collapse happens after animation finishes
expect(mockTreeService.collapse).toHaveBeenCalledTimes(0);
node.closeAnimationDone.emit();
expect(mockTreeService.collapse).toHaveBeenCalledTimes(1);
expect(mockTreeService.collapse).toHaveBeenCalledWith(node);
expect(node.expandedChange.emit).toHaveBeenCalledTimes(2);
expect(node.expandedChange.emit).toHaveBeenCalledWith(false);
expect(mockTree.nodeCollapsed.emit).toHaveBeenCalledTimes(1);
expect(mockTree.nodeCollapsed.emit).toHaveBeenCalledWith(edArgs);
spyOn(node, 'expand');
spyOn(node, 'collapse');
node.toggle();
expect(node.expand).toHaveBeenCalledTimes(1);
expect(node.collapse).toHaveBeenCalledTimes(0);
spyOn(mockTreeService, 'isExpanded').and.returnValue(true);
node.toggle();
expect(node.expand).toHaveBeenCalledTimes(1);
expect(node.collapse).toHaveBeenCalledTimes(1);
});
it('Should properly get tree display density token', () => {
const node = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, null);
expect(node.isCosy).toBeFalse();
expect(node.isCompact).toBeFalse();
spyOnProperty(mockTree, 'displayDensity', 'get').and.returnValue(DisplayDensity.cosy);
expect(node.isCosy).toBeTrue();
expect(node.isCompact).toBeFalse();
spyOnProperty(mockTree, 'displayDensity', 'get').and.returnValue(DisplayDensity.compact);
expect(node.isCosy).toBeFalse();
expect(node.isCompact).toBeTrue();
});
it('Should have correct path to node, regardless if node has parent or not', () => {
const node = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, null);
expect(node.path).toEqual([node]);
const childNode = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, node);
expect(childNode.path).toEqual([node, childNode]);
});
it('Should clear itself from selection service on destroy', () => {
const node = new IgxTreeNodeComponent<any>(mockTree, mockSelectionService, mockTreeService,
mockNavService, mockCdr, mockAnimationService, mockElementRef, null);
node.ngOnDestroy();
expect(mockSelectionService.ensureStateOnNodeDelete).toHaveBeenCalledWith(node);
});
});
describe('IgxTreeService', () => {
it('Should properly register tree', () => {
const service = new IgxTreeService();
expect((service as any).tree).toBe(undefined);
const mockTree = jasmine.createSpyObj<any>('tree', ['findNodes']);
service.register(mockTree);
expect((service as any).tree).toBe(mockTree);
});
it('Should keep a proper collection of expanded and collapsing nodes at all time, firing `expandedChange` when needed', () => {
const service = new IgxTreeService();
const mockTree = jasmine.createSpyObj<any>('tree', ['findNodes'], {
_singleBranchExpand: false,
get singleBranchExpand(): boolean {
return this._singleBranchExpand;
},
set singleBranchExpand(val: boolean) {
this._singleBranchExpand = val;
}
});
service.register(mockTree);
spyOn(service.expandedNodes, 'add').and.callThrough();
spyOn(service.expandedNodes, 'delete').and.callThrough();
spyOn(service.collapsingNodes, 'add').and.callThrough();
spyOn(service.collapsingNodes, 'delete').and.callThrough();
expect(service.expandedNodes.size).toBe(0);
expect(service.collapsingNodes.size).toBe(0);
const mockNode = jasmine.createSpyObj<any>('node', ['collapse'], {
expandedChange: jasmine.createSpyObj('emitter', ['emit'])
});
service.expand(mockNode);
expect(service.collapsingNodes.delete).toHaveBeenCalledWith(mockNode);
expect(service.collapsingNodes.delete).toHaveBeenCalledTimes(1);
expect(service.expandedNodes.add).toHaveBeenCalledWith(mockNode);
expect(mockNode.expandedChange.emit).toHaveBeenCalledTimes(1);
expect(mockNode.expandedChange.emit).toHaveBeenCalledWith(true);
expect(service.expandedNodes.size).toBe(1);
expect(mockNode.collapse).not.toHaveBeenCalled();
service.expand(mockNode);
expect(service.collapsingNodes.delete).toHaveBeenCalledTimes(2);
expect(mockNode.expandedChange.emit).toHaveBeenCalledTimes(1);
expect(service.expandedNodes.size).toBe(1);
service.collapse(mockNode);
expect(mockNode.expandedChange.emit).toHaveBeenCalledTimes(2);
expect(mockNode.expandedChange.emit).toHaveBeenCalledWith(false);
expect(service.collapsingNodes.delete).toHaveBeenCalledWith(mockNode);
expect(service.collapsingNodes.delete).toHaveBeenCalledTimes(3);
expect(service.expandedNodes.delete).toHaveBeenCalledTimes(1);
expect(service.expandedNodes.delete).toHaveBeenCalledWith(mockNode);
expect(service.expandedNodes.size).toBe(0);
service.collapse(mockNode);
expect(mockNode.expandedChange.emit).toHaveBeenCalledTimes(2);
expect(service.collapsingNodes.delete).toHaveBeenCalledTimes(4);
expect(service.expandedNodes.delete).toHaveBeenCalledTimes(2);
const mockArray = [];
for (let i = 0; i < 5; i++) {
const node = jasmine.createSpyObj('node', ['collapse'], {
_expanded: false,
get expanded() {
return this._expanded;
},
set expanded(val: boolean) {
this._expanded = val;
}
});
node.spyProp = spyOnProperty(node, 'expanded', 'set').and.callThrough();
mockArray.push(node);
}
spyOn(mockTree, 'findNodes').and.returnValue(mockArray);
spyOnProperty(mockTree, 'singleBranchExpand', 'get').and.returnValue(true);
service.expand(mockNode);
mockArray.forEach(n => {
expect((n as any).spyProp).toHaveBeenCalledWith(false);
expect(n.collapse).not.toHaveBeenCalled();
});
service.collapse(mockNode);
service.expand(mockNode, true);
mockArray.forEach(n => {
expect(n.collapse).toHaveBeenCalled();
expect(n.collapse).toHaveBeenCalledTimes(1);
});
expect(service.collapsingNodes.size).toBe(0);
service.collapsing(mockNode);
expect(service.collapsingNodes.size).toBe(1);
service.collapse(mockNode);
spyOnProperty(mockTree, 'singleBranchExpand', 'get').and.returnValue(true);
spyOn(mockTree, 'findNodes').and.returnValue(null);
service.expand(mockNode, true);
expect(mockTree.findNodes).toHaveBeenCalledWith(mockNode, (service as any).siblingComparer);
mockArray.forEach(n => {
expect(n.collapse).toHaveBeenCalledTimes(1);
});
});
});
});
describe('Rendering Tests', () => {
let fix: ComponentFixture<IgxTreeSampleComponent>;
let tree: IgxTreeComponent;
beforeAll(
waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxTreeSampleComponent
]
}).compileComponents();
})
);
beforeEach(() => {
fix = TestBed.createComponent<IgxTreeSampleComponent>(IgxTreeSampleComponent);
fix.detectChanges();
tree = fix.componentInstance.tree;
});
describe('General', () => {
it('Should only render node children', () => {
const treeEl: HTMLElement = fix.debugElement.queryAll(By.css(`.${TREE_ROOT_CLASS}`))[0].nativeElement;
let childNodes = treeEl.children;
expect(childNodes.length).toBe(5);
for (let i = 0; i < childNodes.length; i++) {
expect(childNodes.item(i).tagName === NODE_TAG);
}
fix.componentInstance.divChild = true;
childNodes = treeEl.children;
expect(childNodes.length).toBe(5);
for (let i = 0; i < childNodes.length; i++) {
expect(childNodes.item(i).tagName === NODE_TAG);
}
});
it('Should not render collapsed nodes', () => {
let allNodes: DebugElement[] = fix.debugElement.queryAll(By.css(NODE_TAG));
expect(allNodes.length).toBe(5);
tree.nodes.first.expanded = true;
fix.detectChanges();
allNodes = fix.debugElement.queryAll(By.css(NODE_TAG));
expect(allNodes.length).toBe(10);
const visibleNodes = tree.nodes.filter(n => allNodes.findIndex(e => e.nativeElement === n.nativeElement) > -1);
visibleNodes.forEach(n => {
expect(n.level === 0 || n.parentNode.expanded === true).toBeTruthy();
});
});
it('Should apply proper node classes depending on tree displayDenisty', () => {
pending('Test not implemented');
});
it('Should do nothing when calling expand()/collapse() on expanded/collapsed node', fakeAsync(() => {
const expandingSpy = spyOn(tree.nodeExpanding, 'emit').and.callThrough();
const collapsingSpy = spyOn(tree.nodeCollapsing, 'emit').and.callThrough();
tree.nodes.first.collapse();
expect(expandingSpy).not.toHaveBeenCalled();
tree.nodes.first.expanded = true;
tree.nodes.first.expand();
expect(collapsingSpy).not.toHaveBeenCalled();
}));
it('Should properly emit state toggle events', fakeAsync(() => {
// node event spies
const collapsingSpy = spyOn(tree.nodeCollapsing, 'emit').and.callThrough();
const expandingSpy = spyOn(tree.nodeExpanding, 'emit').and.callThrough();
const expandedSpy = spyOn(tree.nodeExpanded, 'emit').and.callThrough();
const collapsedSpy = spyOn(tree.nodeCollapsed, 'emit').and.callThrough();
expect(collapsingSpy).not.toHaveBeenCalled();
expect(expandingSpy).not.toHaveBeenCalled();
expect(expandedSpy).not.toHaveBeenCalled();
expect(collapsedSpy).not.toHaveBeenCalled();
tree.nodes.first.expand();
expect(expandingSpy).toHaveBeenCalledTimes(1);
expect(collapsingSpy).not.toHaveBeenCalled();
expect(expandedSpy).not.toHaveBeenCalled();
expect(collapsedSpy).not.toHaveBeenCalled();
tick();
fix.detectChanges();
tick();
expect(expandingSpy).toHaveBeenCalledTimes(1);
expect(expandedSpy).toHaveBeenCalledTimes(1);
expect(collapsingSpy).not.toHaveBeenCalled();
expect(collapsedSpy).not.toHaveBeenCalled();
tree.nodes.first.collapse();
expect(expandingSpy).toHaveBeenCalledTimes(1);
expect(expandedSpy).toHaveBeenCalledTimes(1);
expect(collapsingSpy).toHaveBeenCalledTimes(1);
expect(collapsedSpy).not.toHaveBeenCalled();
tick();
fix.detectChanges();
tick();
expect(expandingSpy).toHaveBeenCalledTimes(1);
expect(expandedSpy).toHaveBeenCalledTimes(1);
expect(collapsingSpy).toHaveBeenCalledTimes(1);
expect(collapsedSpy).toHaveBeenCalledTimes(1);
// cancel ingEvents
const unsub$ = new Subject<void>();
tree.nodeExpanding.pipe(takeUntil(unsub$)).subscribe(e => {
e.cancel = true;
});
tree.nodes.first.expand();
expect(expandingSpy).toHaveBeenCalledTimes(2);
expect(expandedSpy).toHaveBeenCalledTimes(1);
tick();
fix.detectChanges();
tick();
expect(expandingSpy).toHaveBeenCalledTimes(2);
expect(expandedSpy).toHaveBeenCalledTimes(1);
tree.nodes.first.expanded = true;
unsub$.next();
tree.nodeCollapsing.pipe(takeUntil(unsub$)).subscribe(e => {
e.cancel = true;
});
tree.nodes.first.collapse();
expect(collapsingSpy).toHaveBeenCalledTimes(2);
expect(collapsedSpy).toHaveBeenCalledTimes(1);
tick();
fix.detectChanges();
tick();
expect(collapsingSpy).toHaveBeenCalledTimes(2);
expect(collapsedSpy).toHaveBeenCalledTimes(1);
unsub$.next();
unsub$.complete();
}));
it('Should collapse all sibling nodes when `singleBranchExpand` is set and node is toggled', fakeAsync(() => {
pending('Causes jasmine to hang');
tree.rootNodes.forEach((n, index) => index > 0 ? n.expanded = true : n.expanded = false);
fix.detectChanges();
expect(tree.nodes.filter(n => n.expanded).length).toBe(4);
tree.singleBranchExpand = true;
tree.rootNodes.forEach(n => {
spyOn(n.expandedChange, 'emit').and.callThrough();
});
const collapsingSpy = spyOn(tree.nodeCollapsing, 'emit').and.callThrough();
const expandingSpy = spyOn(tree.nodeExpanding, 'emit').and.callThrough();
const expandedSpy = spyOn(tree.nodeCollapsed, 'emit').and.callThrough();
const collapsedSpy = spyOn(tree.nodeExpanded, 'emit').and.callThrough();
// should not emit event when nodes are toggled through input
tree.rootNodes[0].expanded = true;
fix.detectChanges();
tree.rootNodes.forEach(n => {
expect(n.expandedChange.emit).toHaveBeenCalled();
});
expect(expandingSpy).not.toHaveBeenCalled();
expect(collapsingSpy).not.toHaveBeenCalled();
expect(expandedSpy).not.toHaveBeenCalled();
expect(collapsedSpy).not.toHaveBeenCalled();
expect(tree.nodes.filter(n => n.expanded).length).toBe(1);
const expandedArgs = {
node: tree.rootNodes[1],
owner: tree
};
const collapsedArgs = {
node: tree.rootNodes[0],
owner: tree
};
tree.rootNodes[1].expand();
tick();
fix.detectChanges();
expect(expandingSpy).toHaveBeenCalledTimes(1);
expect(expandingSpy).toHaveBeenCalledWith(Object.assign({}, expandedArgs, { cancel: false }));
expect(collapsingSpy).toHaveBeenCalledTimes(1);
expect(expandingSpy).toHaveBeenCalledWith(Object.assign({}, collapsedArgs, { cancel: false }));
expect(expandedSpy).toHaveBeenCalledTimes(1);
expect(expandedSpy).toHaveBeenCalledWith(expandedArgs);
expect(collapsedSpy).toHaveBeenCalledTimes(1);
expect(collapsedSpy).toHaveBeenCalledWith(collapsedArgs);
tree.singleBranchExpand = false;
fix.detectChanges();
const deepNode = tree.findNodes('2-1', (_id: '2-1', n: IgxTreeNodeComponent<any>) => n.data.id === '2-1')[0];
expect(deepNode).not.toBeNull();
fix.componentInstance.expandToNode(deepNode);
const siblingNodes = tree.findNodes(deepNode,
(tn: IgxTreeNodeComponent<any>, n: IgxTreeNodeComponent<any>) => n.level === tn.level && n.parentNode === tn.parentNode
);
expect(siblingNodes.length).toBe(5);
siblingNodes.forEach(n => n.expanded = true);
fix.detectChanges();
expect(tree.nodes.filter(e => e.expanded).length).toBe(7);
siblingNodes[0].expanded = false;
fix.detectChanges();
expect(tree.nodes.filter(e => e.expanded).length).toBe(6);
tree.singleBranchExpand = true;
siblingNodes[0].expanded = true;
fix.detectChanges();
expect(tree.nodes.filter(e => e.expanded).length).toBe(3);
const nodeLevels = tree.nodes.filter(n => n.expanded).map(n => n.level);
expect(nodeLevels).toEqual([0, 1, 2]);
}));
});
describe('ARIA', () => {
it('Should render proper roles for tree and nodes', () => {
pending('Test not implemented');
});
it('Should render proper label for expand/collapse indicator, depending on node state', () => {
pending('Test not implemented');
});
it('Should render proper roles for nodes containing link children', () => {
pending('Test not implemented');
});
});
});
});
({
template: `
<igx-tree>
<igx-tree-node [(expanded)]="node.expanded" [data]="node" *ngFor="let node of data">
{{ node.label }}
<igx-tree-node [(expanded)]="child.expanded" [data]="child" *ngFor="let child of node.children">
{{ child.label }}
<igx-tree-node [(expanded)]="leafChild.expanded" [data]="leafChild" *ngFor="let leafChild of child.children">
{{ leafChild.label }}
</igx-tree-node>
</igx-tree-node>
</igx-tree-node>
<div *ngIf="divChild"></div>
</igx-tree>
`,
standalone: true,
imports: [IgxTreeComponent, NgIf, IgxTreeNodeComponent, NgFor]
})
class IgxTreeSampleComponent {
(IgxTreeComponent)
public tree: IgxTreeComponent;
public divChild = true;
public data = createHierarchicalData(5, 3);
public expandToNode(node: IgxTreeNodeComponent<any>): void {
node.path.forEach(n => n.expanded = true);
}
}
class MockDataItem {
public selected = false;
public expanded = false;
public children: MockDataItem[] = [];
constructor(public id: string, public label: string) {
}
}
const createHierarchicalData = (siblings: number, depth: number): MockDataItem[] => {
let id = 0;
const returnArr = [];
for (let i = 0; i < siblings; i++) {
const item = new MockDataItem(`${depth}-${id}`, `Label ${depth}-${id}`);
id++;
returnArr.push(item);
if (depth > 0) {
item.children = createHierarchicalData(siblings, depth - 1);
}
}
return returnArr;
};