import { CdkTreeModule } from '@angular/cdk/tree'; import { CUSTOM_ELEMENTS_SCHEMA, DebugElement, } from '@angular/core'; import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { RouterLinkWithHref } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateLoader, TranslateModule, } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; import { buildPaginatedList } from '../../core/data/paginated-list.model'; import { Collection } from '../../core/shared/collection.model'; import { Community } from '../../core/shared/community.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { isEmpty, isNotEmpty, } from '../../shared/empty.util'; import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { TruncatableComponent } from '../../shared/truncatable/truncatable.component'; import { TruncatablePartComponent } from '../../shared/truncatable/truncatable-part/truncatable-part.component'; import { CommunityListService, showMoreFlatNode, toFlatNode, } from '../community-list-service'; import { FlatNode } from '../flat-node.model'; import { CommunityListComponent } from './community-list.component'; describe('CommunityListComponent', () => { let component: CommunityListComponent; let fixture: ComponentFixture<CommunityListComponent>; const mockSubcommunities1Page1 = [Object.assign(new Community(), { id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88', uuid: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88', name: 'subcommunity1', }), Object.assign(new Community(), { id: '59ee713b-ee53-4220-8c3f-9860dc84fe33', uuid: '59ee713b-ee53-4220-8c3f-9860dc84fe33', name: 'subcommunity2', }), ]; const mockCollectionsPage1 = [ Object.assign(new Collection(), { id: 'e9dbf393-7127-415f-8919-55be34a6e9ed', uuid: 'e9dbf393-7127-415f-8919-55be34a6e9ed', name: 'collection1', }), Object.assign(new Collection(), { id: '59da2ff0-9bf4-45bf-88be-e35abd33f304', uuid: '59da2ff0-9bf4-45bf-88be-e35abd33f304', name: 'collection2', }), ]; const mockCollectionsPage2 = [ Object.assign(new Collection(), { id: 'a5159760-f362-4659-9e81-e3253ad91ede', uuid: 'a5159760-f362-4659-9e81-e3253ad91ede', name: 'collection3', }), Object.assign(new Collection(), { id: 'a392e16b-fcf2-400a-9a88-53ef7ecbdcd3', uuid: 'a392e16b-fcf2-400a-9a88-53ef7ecbdcd3', name: 'collection4', }), ]; const mockTopCommunitiesWithChildrenArrays = [ { id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f', uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f', subcommunities: mockSubcommunities1Page1, collections: [], }, { id: '9076bd16-e69a-48d6-9e41-0238cb40d863', uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863', subcommunities: [], collections: [...mockCollectionsPage1, ...mockCollectionsPage2], }, { id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24', uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24', subcommunities: [], collections: [], }]; const mockTopFlatnodesUnexpanded: FlatNode[] = [ toFlatNode( Object.assign(new Community(), { id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f', uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f', subcommunities: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), mockSubcommunities1Page1)), collections: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), name: 'community1', }), observableOf(true), 0, false, null, ), toFlatNode( Object.assign(new Community(), { id: '9076bd16-e69a-48d6-9e41-0238cb40d863', uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863', subcommunities: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), collections: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [...mockCollectionsPage1, ...mockCollectionsPage2])), name: 'community2', }), observableOf(true), 0, false, null, ), toFlatNode( Object.assign(new Community(), { id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24', uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24', subcommunities: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), collections: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), name: 'community3', }), observableOf(false), 0, false, null, ), ]; let communityListServiceStub; beforeEach(waitForAsync(() => { communityListServiceStub = { pageSize: 2, expandedNodes: [], loadingNode: null, getLoadingNodeFromStore() { return observableOf(this.loadingNode); }, getExpandedNodesFromStore() { return observableOf(this.expandedNodes); }, saveCommunityListStateToStore(expandedNodes, loadingNode) { this.expandedNodes = expandedNodes; this.loadingNode = loadingNode; }, loadCommunities(options, expandedNodes) { let flatnodes; let showMoreTopComNode = false; flatnodes = [...mockTopFlatnodesUnexpanded]; const currentPage = options.currentPage; const elementsPerPage = this.pageSize; let endPageIndex = (currentPage * elementsPerPage); if (endPageIndex >= flatnodes.length) { endPageIndex = flatnodes.length; } else { showMoreTopComNode = true; } if (expandedNodes === null || isEmpty(expandedNodes)) { if (showMoreTopComNode) { return observableOf([...mockTopFlatnodesUnexpanded.slice(0, endPageIndex), showMoreFlatNode(`community-${uuidv4()}`, 0, null)]); } else { return observableOf(mockTopFlatnodesUnexpanded.slice(0, endPageIndex)); } } else { flatnodes = []; const topFlatnodes = mockTopFlatnodesUnexpanded.slice(0, endPageIndex); topFlatnodes.map((topNode: FlatNode) => { flatnodes = [...flatnodes, topNode]; const expandedParent: FlatNode = expandedNodes.find((expandedNode: FlatNode) => expandedNode.id === topNode.id); if (isNotEmpty(expandedParent)) { const matchingTopComWithArrays = mockTopCommunitiesWithChildrenArrays.find((topcom) => topcom.id === topNode.id); if (isNotEmpty(matchingTopComWithArrays)) { const possibleSubcoms: Community[] = matchingTopComWithArrays.subcommunities; let subComFlatnodes = []; possibleSubcoms.map((subcom: Community) => { subComFlatnodes = [...subComFlatnodes, toFlatNode(subcom, observableOf(false), topNode.level + 1, false, topNode)]; }); const possibleColls: Collection[] = matchingTopComWithArrays.collections; let collFlatnodes = []; possibleColls.map((coll: Collection) => { collFlatnodes = [...collFlatnodes, toFlatNode(coll, observableOf(false), topNode.level + 1, false, topNode)]; }); if (isNotEmpty(subComFlatnodes)) { const endSubComIndex = this.pageSize * expandedParent.currentCommunityPage; flatnodes = [...flatnodes, ...subComFlatnodes.slice(0, endSubComIndex)]; if (subComFlatnodes.length > endSubComIndex) { flatnodes = [...flatnodes, showMoreFlatNode(`community-${uuidv4()}`, topNode.level + 1, expandedParent)]; } } if (isNotEmpty(collFlatnodes)) { const endColIndex = this.pageSize * expandedParent.currentCollectionPage; flatnodes = [...flatnodes, ...collFlatnodes.slice(0, endColIndex)]; if (collFlatnodes.length > endColIndex) { flatnodes = [...flatnodes, showMoreFlatNode(`collection-${uuidv4()}`, topNode.level + 1, expandedParent)]; } } } } }); if (showMoreTopComNode) { flatnodes = [...flatnodes, showMoreFlatNode(`community-${uuidv4()}`, 0, null)]; } return observableOf(flatnodes); } }, }; TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateLoaderMock, }, }), CdkTreeModule, RouterTestingModule, RouterLinkWithHref, CommunityListComponent, ], providers: [CommunityListComponent, { provide: CommunityListService, useValue: communityListServiceStub }], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) .overrideComponent(CommunityListComponent, { remove: { imports: [ ThemedLoadingComponent, TruncatableComponent, TruncatablePartComponent, ] }, }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(CommunityListComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', inject([CommunityListComponent], (comp: CommunityListComponent) => { expect(comp).toBeTruthy(); })); it('should render a cdk tree with the first elementsPerPage (2) nr of top level communities, unexpanded', () => { const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); const allNodes = [...expandableNodesFound, ...childlessNodesFound]; expect(allNodes.length).toEqual(2); mockTopFlatnodesUnexpanded.slice(0, 2).map((topFlatnode: FlatNode) => { expect(allNodes.find((foundEl) => { return (foundEl.nativeElement.textContent.trim() === topFlatnode.name); })).toBeTruthy(); }); }); it('show more node is present at end of nodetree', () => { const showMoreEl = fixture.debugElement.queryAll(By.css('.show-more-node')); expect(showMoreEl.length).toEqual(1); expect(showMoreEl).toBeTruthy(); }); it('should not render the show more button as an empty link', () => { const debugElements = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); expect(debugElements).toBeTruthy(); }); describe('when show more of top communities is clicked', () => { beforeEach(fakeAsync(() => { const showMoreLink = fixture.debugElement.query(By.css('.show-more-node .btn-outline-primary')); showMoreLink.triggerEventHandler('click', { preventDefault: () => {/**/ }, }); tick(); fixture.detectChanges(); })); it('tree contains maximum of currentPage (2) * (2) elementsPerPage of first top communities, or less if there are less communities (3)', () => { const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); const allNodes = [...expandableNodesFound, ...childlessNodesFound]; expect(allNodes.length).toEqual(3); mockTopFlatnodesUnexpanded.map((topFlatnode: FlatNode) => { expect(allNodes.find((foundEl) => { return (foundEl.nativeElement.textContent.trim() === topFlatnode.name); })).toBeTruthy(); }); }); it('show more node is gone from end of nodetree', () => { const showMoreEl = fixture.debugElement.queryAll(By.css('.show-more-node')); expect(showMoreEl.length).toEqual(0); }); }); describe('when first expandable node is expanded', () => { let allNodes; beforeEach(fakeAsync(() => { const chevronExpand = fixture.debugElement.query(By.css('.expandable-node button')); const chevronExpandSpan = fixture.debugElement.query(By.css('.expandable-node button span')); if (chevronExpandSpan.nativeElement.classList.contains('fa-chevron-right')) { chevronExpand.nativeElement.click(); tick(); fixture.detectChanges(); } const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); allNodes = [...expandableNodesFound, ...childlessNodesFound]; })); describe('children of first expandable node are added to tree (page-limited)', () => { it('tree contains page-limited topcoms (2) and children of first expandable node (2subcoms)', () => { expect(allNodes.length).toEqual(4); mockTopFlatnodesUnexpanded.slice(0, 2).map((topFlatnode: FlatNode) => { expect(allNodes.find((foundEl) => { return (foundEl.nativeElement.textContent.trim() === topFlatnode.name); })).toBeTruthy(); }); mockSubcommunities1Page1.map((subcom) => { expect(allNodes.find((foundEl) => { return (foundEl.nativeElement.textContent.trim() === subcom.name); })).toBeTruthy(); }); }); }); }); describe('second top community node is expanded and has more children (collections) than page size of collection', () => { describe('children of second top com are added (page-limited pageSize 2)', () => { let allNodes: DebugElement[]; beforeEach(fakeAsync(() => { const toggleButtons: DebugElement[] = fixture.debugElement.queryAll(By.css('.expandable-node button')); const toggleButtonText: DebugElement = toggleButtons[1].query(By.css('span')); expect(toggleButtonText).not.toBeNull(); if (toggleButtonText.nativeElement.classList.contains('fa-chevron-right')) { toggleButtons[1].nativeElement.click(); tick(); fixture.detectChanges(); } const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); allNodes = [...expandableNodesFound, ...childlessNodesFound]; })); it('tree contains 2 (page-limited) top com, 2 (page-limited) coll of 2nd top com, a show more for those page-limited coll and show more for page-limited top com', () => { const allNodeNames: string[] = allNodes.map((node: DebugElement) => node.nativeElement.innerText.trim()); expect(allNodes.length).toEqual(4); const flatNodes: string[] = mockTopFlatnodesUnexpanded.slice(0, 2).map((flatNode: FlatNode) => flatNode.name); for (const flatNode of flatNodes) { expect(allNodeNames).toContain(flatNode); } expect(flatNodes.length).toBe(2); const page1CollectionNames: string[] = mockCollectionsPage1.map((collection: Collection) => collection.name); for (const collectionName of page1CollectionNames) { expect(allNodeNames).toContain(collectionName); } expect(page1CollectionNames.length).toBe(2); const showMoreEl = fixture.debugElement.queryAll(By.css('.show-more-node')); expect(showMoreEl.length).toEqual(2); }); }); }); });