From eccbb6619c406ab24f24564e39311cdfa94c691d Mon Sep 17 00:00:00 2001 From: Alex Malkevich Date: Wed, 27 Feb 2019 11:34:23 +0100 Subject: [PATCH] test(menu): add unit tests for rendering logic --- .../src/lib/dynamic-menu.service.spec.ts | 6 +- .../src/lib/dynamic-menu.service.ts | 10 +- .../dynamic-menu-items.component.spec.ts | 195 ++++++++++++++++++ .../dynamic-menu-items.component.ts | 12 +- .../dynamic-menu.component.spec.ts | 1 + 5 files changed, 210 insertions(+), 14 deletions(-) diff --git a/projects/dynamic-menu/src/lib/dynamic-menu.service.spec.ts b/projects/dynamic-menu/src/lib/dynamic-menu.service.spec.ts index fee2cef..40d8a7a 100644 --- a/projects/dynamic-menu/src/lib/dynamic-menu.service.spec.ts +++ b/projects/dynamic-menu/src/lib/dynamic-menu.service.spec.ts @@ -96,7 +96,7 @@ describe('Service: DynamicMenu', () => { expect(menu[1].data.menu.label).toBe('Route 2'); })); - it('should resolve `subMenuComponent` from map', fakeAsync(() => { + it('should resolve `data.menu.subMenuComponent` from map', fakeAsync(() => { class Comp1 {} class Comp2 {} @@ -141,13 +141,13 @@ describe('Service: DynamicMenu', () => { expect(menu[0].path).toBe('route1'); expect(menu[0].data.menu.label).toBe('Route 1'); - expect(menu[0].subMenuComponent).toBe(Comp1); + expect(menu[0].data.menu.subMenuComponent).toBe(Comp1); const subMenu = menu[0].data.menu.children; expect(subMenu.length).toBe(1); expect(subMenu[0].path).toBe('route2'); expect(subMenu[0].data.menu.label).toBe('Route 2'); - expect(subMenu[0].subMenuComponent).toBe(Comp2); + expect(subMenu[0].data.menu.subMenuComponent).toBe(Comp2); })); it('should update menu when `listenForConfigChanges` is `true` and lazy module resolved', fakeAsync(() => { diff --git a/projects/dynamic-menu/src/lib/dynamic-menu.service.ts b/projects/dynamic-menu/src/lib/dynamic-menu.service.ts index 7e68d8c..e976684 100644 --- a/projects/dynamic-menu/src/lib/dynamic-menu.service.ts +++ b/projects/dynamic-menu/src/lib/dynamic-menu.service.ts @@ -107,10 +107,12 @@ export class DynamicMenuService { ? parentConfig.fullUrl || [parentConfig.path] : []; - config.data.menu.subMenuComponent = this.resolveSubMenuComponent( - config, - subMenuMap, - ); + if (config.data && config.data.menu) { + config.data.menu.subMenuComponent = this.resolveSubMenuComponent( + config, + subMenuMap, + ); + } return { ...config, diff --git a/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.spec.ts b/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.spec.ts index eb107bf..77a8f48 100644 --- a/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.spec.ts +++ b/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.spec.ts @@ -1,9 +1,15 @@ import { Component } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { DynamicMenuService } from '../../dynamic-menu.service'; import { DynamicMenuTemplateContext } from '../context-template'; import { DynamicMenuItemsComponent } from './dynamic-menu-items.component'; +class DynamicMenuServiceMock { + isActive = jasmine.createSpy('isActive spy'); +} + @Component({ selector: 'ndm-host', template: ` @@ -20,7 +26,11 @@ describe('DynamicMenuItemsComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ + imports: [RouterTestingModule], declarations: [DynamicMenuItemsComponent, HostComponent], + providers: [ + { provide: DynamicMenuService, useClass: DynamicMenuServiceMock }, + ], }); }); @@ -68,9 +78,194 @@ describe('DynamicMenuItemsComponent', () => { expect(fixture.nativeElement.textContent).toBe('Tpl: var'); }); + + describe('`config.showChildrenIfActivated`', () => { + describe('set to `true`', () => { + beforeEach(() => { + hostComp.getCtx.and.callFake( + (tpl: any) => + new DynamicMenuTemplateContext( + tpl, + {}, + { + fullUrl: 'full-url' as any, + data: { + menu: { + showChildrenIfActivated: true, + label: '', + children: [], + }, + }, + }, + ), + ); + }); + + it('should call `dynamicMenuService.isActive()` with `config.fullUrl`', () => { + fixture.detectChanges(); + + expect(getDynamicMenuService().isActive).toHaveBeenCalledWith( + 'full-url', + ); + }); + + it('should render tpl if `dynamicMenuService.isActive` returns `true`', () => { + getDynamicMenuService().isActive.and.returnValue(true); + + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Tpl: '); + }); + + it('should NOT render tpl if `dynamicMenuService.isActive` returns `false`', () => { + getDynamicMenuService().isActive.and.returnValue(false); + + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe(''); + }); + }); + + describe('set to `false`', () => { + beforeEach(() => { + hostComp.getCtx.and.callFake( + (tpl: any) => + new DynamicMenuTemplateContext( + tpl, + {}, + { + fullUrl: 'full-url' as any, + data: { + menu: { + showChildrenIfActivated: false, + label: '', + children: [], + }, + }, + }, + ), + ); + }); + + it('should NOT call `dynamicMenuService.isActive()`', () => { + fixture.detectChanges(); + + expect(getDynamicMenuService().isActive).not.toHaveBeenCalled(); + }); + + it('should always render tpl', () => { + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Tpl: '); + }); + }); + }); + + describe('`config.showChildrenIfChildActivated`', () => { + describe('set to `true`', () => { + beforeEach(() => { + hostComp.getCtx.and.callFake( + (tpl: any) => + new DynamicMenuTemplateContext( + tpl, + {}, + { + fullUrl: 'full-url' as any, + data: { + menu: { + showChildrenIfChildActivated: true, + label: '', + children: [], + }, + }, + }, + ), + ); + }); + + it('should call `dynamicMenuService.isActive()` with `config.fullUrl, true`', () => { + fixture.detectChanges(); + + expect(getDynamicMenuService().isActive).toHaveBeenCalledWith( + 'full-url', + true, + ); + }); + + it('should call `dynamicMenuService.isActive()` with `config.fullUrl`', () => { + fixture.detectChanges(); + + expect(getDynamicMenuService().isActive).toHaveBeenCalledWith( + 'full-url', + ); + }); + + it('should render tpl if `dynamicMenuService.isActive` first returns `false` and second `true`', () => { + getDynamicMenuService().isActive.and.returnValues(false, true); + + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Tpl: '); + }); + + it('should NOT render tpl if `dynamicMenuService.isActive` first returns `true`', () => { + getDynamicMenuService().isActive.and.returnValue(false); + + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe(''); + }); + + it('should NOT render tpl if `dynamicMenuService.isActive` second returns `false`', () => { + getDynamicMenuService().isActive.and.returnValues(false, false); + + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe(''); + }); + }); + + describe('set to `false`', () => { + beforeEach(() => { + hostComp.getCtx.and.callFake( + (tpl: any) => + new DynamicMenuTemplateContext( + tpl, + {}, + { + fullUrl: 'full-url' as any, + data: { + menu: { + showChildrenIfChildActivated: false, + label: '', + children: [], + }, + }, + }, + ), + ); + }); + + it('should NOT call `dynamicMenuService.isActive()`', () => { + fixture.detectChanges(); + + expect(getDynamicMenuService().isActive).not.toHaveBeenCalled(); + }); + + it('should always render tpl', () => { + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Tpl: '); + }); + }); + }); }); }); function overrideHostTpl(tpl: string) { TestBed.overrideTemplate(HostComponent, tpl); } + +function getDynamicMenuService(): DynamicMenuServiceMock { + return TestBed.get(DynamicMenuService); +} diff --git a/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.ts b/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.ts index 6d24dc3..780563f 100644 --- a/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.ts +++ b/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu-items/dynamic-menu-items.component.ts @@ -23,7 +23,7 @@ export interface NgView { changeDetection: ChangeDetectionStrategy.OnPush, }) export class DynamicMenuItemsComponent implements OnInit { - ctx: DynamicMenuTemplateContext | undefined; + ctx!: DynamicMenuTemplateContext; navigationEnd$ = this.router.events.pipe( filter(e => e instanceof NavigationEnd), @@ -41,11 +41,13 @@ export class DynamicMenuItemsComponent implements OnInit { ) {} ngOnInit(): void { - this.ctx = this.getTplContext((this.vcr as any)._view); + const ctx = this.getTplContext((this.vcr as any)._view); - if (!this.ctx) { + if (!ctx) { throw Error(`DynamicMenuItemsComponent: Used outside of context!`); } + + this.ctx = ctx; } private getTplContext(view: NgView | undefined) { @@ -59,10 +61,6 @@ export class DynamicMenuItemsComponent implements OnInit { } private shouldRender() { - if (!this.ctx) { - return false; - } - const { parentConfig } = this.ctx; if (parentConfig) { diff --git a/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu.component.spec.ts b/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu.component.spec.ts index efc7153..2a0603b 100644 --- a/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu.component.spec.ts +++ b/projects/dynamic-menu/src/lib/dynamic-menu/dynamic-menu.component.spec.ts @@ -13,6 +13,7 @@ import { DynamicMenuComponent } from './dynamic-menu.component'; class DynamicMenuServiceMock { getMenu = jasmine.createSpy('getMenu spy'); + isActive = jasmine.createSpy('isActive spy'); } @Component({