UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

634 lines (527 loc) 27.2 kB
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { Component, ViewChild, PLATFORM_ID } from '@angular/core'; import { By } from '@angular/platform-browser'; import { wait } from '../test-utils/ui-interactions.spec'; import { IgxNavigationDrawerComponent } from './navigation-drawer.component'; import { IgxNavigationService } from '../core/navigation/nav.service'; import { PlatformUtil } from '../core/utils'; import { IgxNavDrawerMiniTemplateDirective, IgxNavDrawerTemplateDirective } from './navigation-drawer.directives'; import { NgIf } from '@angular/common'; // HammerJS simulator from https://github.com/hammerjs/simulator, manual typings TODO declare let Simulator: any; // const oldTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; describe('Navigation Drawer', () => { let widthSpyOverride: jasmine.Spy; // configureTestSuite(); beforeEach(waitForAsync(() => { // jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; TestBed.configureTestingModule({ imports: [ TestComponentPin, TestComponentMini, TestComponent, TestComponentDIComponent ] }).compileComponents(); // Using Window through DI causes AOT error (https://github.com/angular/angular/issues/15640) // so for tests just force override the the `getWindowWidth` widthSpyOverride = spyOn(IgxNavigationDrawerComponent.prototype as any, 'getWindowWidth') .and.returnValue(915 /* chosen at random by fair dice roll*/); })); // afterEach(() => { // jasmine.DEFAULT_TIMEOUT_INTERVAL = oldTimeout; // }); // afterAll(() => { // TestBed.resetTestingModule(); // }); it('should initialize without DI service', waitForAsync(() => { TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); expect(fixture.componentInstance.navDrawer instanceof IgxNavigationDrawerComponent).toBeTruthy(); expect(fixture.componentInstance.navDrawer.state).not.toBeNull(); }); })); it('should initialize with DI service', waitForAsync(() => { TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); expect(fixture.componentInstance.navDrawer).toBeDefined(); expect(fixture.componentInstance.navDrawer instanceof IgxNavigationDrawerComponent).toBeTruthy(); expect(fixture.componentInstance.navDrawer.state instanceof IgxNavigationService) .toBeTruthy(); }); })); it('should initialize with pinThreshold disabled', waitForAsync(() => { const template = `<igx-nav-drawer [pinThreshold]="0"></igx-nav-drawer>`; TestBed.overrideComponent(TestComponent, { set: { template } }); TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); expect(fixture.componentInstance.navDrawer).toBeDefined(); expect(fixture.componentInstance.navDrawer instanceof IgxNavigationDrawerComponent).toBeTruthy(); expect(() => fixture.destroy()).not.toThrow(); }); })); it('should properly initialize all elements and properties', waitForAsync(() => { TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); const domNavDrawer = fixture.debugElement.query(By.css('igx-nav-drawer')).nativeElement; expect(fixture.componentInstance.navDrawer.id).toContain('igx-nav-drawer-'); expect(domNavDrawer.id).toContain('igx-nav-drawer-'); expect(fixture.componentInstance.navDrawer.drawer.classList).toContain('igx-nav-drawer__aside'); expect(fixture.componentInstance.navDrawer.overlay.classList).toContain('igx-nav-drawer__overlay'); expect(fixture.componentInstance.navDrawer.styleDummy.classList).toContain('igx-nav-drawer__style-dummy'); expect(fixture.componentInstance.navDrawer.hasAnimateWidth).toBeFalsy(); fixture.componentInstance.navDrawer.id = 'customNavDrawer'; fixture.detectChanges(); expect(fixture.componentInstance.navDrawer.id).toBe('customNavDrawer'); expect(domNavDrawer.id).toBe('customNavDrawer'); }).catch((reason) => Promise.reject(reason)); })); it('should attach events and register to nav service and detach on destroy', waitForAsync(() => { const template = '<igx-nav-drawer id=\'testNav\'></igx-nav-drawer>'; TestBed.overrideComponent(TestComponentDIComponent, { set: { template } }); // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); const state: IgxNavigationService = fixture.componentInstance.navDrawer.state; const touchManager = fixture.componentInstance.navDrawer.touchManager; expect(state.get('testNav')).toBeDefined(); expect(touchManager.getManagerForElement(document) instanceof Hammer.Manager).toBeTruthy(); fixture.destroy(); expect(state.get('testNav')).toBeUndefined(); expect(touchManager.getManagerForElement(document)).toBe(null); }).catch((reason) => Promise.reject(reason)); })); it('should open and close with API calls', waitForAsync(() => { TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); const drawer: IgxNavigationDrawerComponent = fixture.componentInstance.navDrawer; expect(drawer.isOpen).toBeFalsy(); drawer.open(); expect(drawer.isOpen).toBeTruthy(); drawer.open(); // should do nothing expect(drawer.isOpen).toBeTruthy(); drawer.close(); expect(drawer.isOpen).toBeFalsy(); drawer.close(); // should do nothing expect(drawer.isOpen).toBeFalsy(); drawer.toggle(); expect(drawer.isOpen).toBeTruthy(); drawer.toggle(); expect(drawer.isOpen).toBeFalsy(); }).catch((reason) => Promise.reject(reason)); })); it('async API calls should emit events', waitForAsync(() => { let fixture: ComponentFixture<TestComponentDIComponent>; let drawer; // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); drawer = fixture.componentInstance.navDrawer; spyOn(drawer.closing, 'emit'); spyOn(drawer.closed, 'emit'); spyOn(drawer.opening, 'emit'); spyOn(drawer.opened, 'emit'); const _re = drawer.open(true); fixture.detectChanges(); fixture.debugElement.children[0].nativeElement.dispatchEvent(new Event('transitionend')); }) .then(() => { expect(drawer.opening.emit).toHaveBeenCalled(); expect(drawer.opened.emit).toHaveBeenCalled(); const _re = drawer.toggle(true); fixture.detectChanges(); fixture.debugElement.children[0].nativeElement.dispatchEvent(new Event('transitionend')); }) .then(() => { expect(drawer.closing.emit).toHaveBeenCalled(); expect(drawer.closed.emit).toHaveBeenCalled(); // resolver(); }); })); it('should properly initialize with min template', waitForAsync(() => { const template = `<igx-nav-drawer> <ng-template igxDrawer></ng-template> <ng-template igxDrawerMini></ng-template> </igx-nav-drawer>`; TestBed.overrideComponent(TestComponentDIComponent, { set: { template } }); // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); expect(fixture.componentInstance.navDrawer.hasAnimateWidth).toBeTruthy(); expect(fixture.debugElement.query((x) => x.nativeNode.nodeName === 'ASIDE').nativeElement.classList) .toContain('igx-nav-drawer__aside--mini'); }).catch((reason) => Promise.reject(reason)); })); it('should update with dynamic min template', waitForAsync(() => { // immediate requestAnimationFrame for testing spyOn(window, 'requestAnimationFrame').and.callFake(callback => { callback(0); return 0; }); const template = `<igx-nav-drawer> <ng-template igxDrawer></ng-template> <ng-template *ngIf="miniView" igxDrawerMini></ng-template> </igx-nav-drawer>`; TestBed.overrideComponent(TestComponentMini, { set: { template } }); let fixture; let asideElem; // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentMini); fixture.detectChanges(); asideElem = fixture.debugElement.query(By.css('.igx-nav-drawer__aside')); expect(asideElem.styles['width']).toEqual('68px'); fixture.componentInstance.miniView = false; fixture.detectChanges(); expect(asideElem.styles['width']).toBeFalsy(); fixture.componentInstance.miniView = true; fixture.detectChanges(); expect(asideElem.styles['width']).toEqual(fixture.componentInstance.navDrawer.miniWidth); }).catch((reason) => Promise.reject(reason)); })); it('should set pin, gestures options', waitForAsync(() => { const template = `<igx-nav-drawer [pin]="pin" pinThreshold="false" [enableGestures]="enableGestures"> </igx-nav-drawer>`; TestBed.overrideComponent(TestComponentPin, { set: { template } }); // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentPin); fixture.detectChanges(); expect(fixture.componentInstance.navDrawer.pin).toBeTruthy(); expect(fixture.debugElement.query((x) => x.nativeNode.nodeName === 'ASIDE').nativeElement.classList) .toContain('igx-nav-drawer__aside--pinned'); expect(fixture.componentInstance.navDrawer.enableGestures).toBe(false); fixture.componentInstance.enableGestures = 'true'; fixture.detectChanges(); expect(fixture.componentInstance.navDrawer.enableGestures).toBeTruthy(); }).catch((reason) => Promise.reject(reason)); })); it('should stay at 100% parent height when pinned', waitForAsync(() => { const template = `<div style="height: 100%; position: relative;"> <igx-nav-drawer [pin]="pin" pinThreshold="false" [enableGestures]="enableGestures"> </igx-nav-drawer> </div>`; TestBed.overrideComponent(TestComponentPin, { set: { template } }); TestBed.compileComponents() .then(() => { document.body.style.overflow = 'hidden'; const fixture = TestBed.createComponent(TestComponentPin); fixture.detectChanges(); const windowHeight = window.innerHeight; const container = fixture.debugElement.query(By.css('div')).nativeElement; const navdrawer = fixture.debugElement.query(By.css('igx-nav-drawer > aside')).nativeElement; fixture.componentInstance.pin = false; fixture.detectChanges(); expect(navdrawer.clientHeight).toEqual(windowHeight); fixture.componentInstance.pin = true; fixture.detectChanges(); expect(navdrawer.clientHeight).toEqual(container.clientHeight); container.style.height = `${windowHeight - 50}px`; fixture.detectChanges(); expect(navdrawer.clientHeight).toEqual(windowHeight - 50); // unpin : fixture.componentInstance.pin = false; fixture.detectChanges(); expect(navdrawer.clientHeight).toEqual(windowHeight); }); })); it('should set flex-basis and order when pinned', waitForAsync(() => { const template = `<igx-nav-drawer [pin]="pin" pinThreshold="false"></igx-nav-drawer>`; TestBed.overrideComponent(TestComponentPin, { set: { template } }); TestBed.compileComponents() .then(() => { const fixture = TestBed.createComponent(TestComponentPin); const drawer = fixture.componentInstance.navDrawer; drawer.isOpen = true; fixture.detectChanges(); const drawerElem = fixture.debugElement.query((x) => x.nativeNode.nodeName === 'IGX-NAV-DRAWER').nativeElement; expect(drawer.pin).toBeTruthy(); expect(drawerElem.style.flexBasis).toEqual(drawer.width); expect(drawerElem.style.order).toEqual('0'); drawer.width = '345px'; drawer.position = 'right'; fixture.detectChanges(); expect(drawerElem.style.flexBasis).toEqual(drawer.width); expect(drawerElem.style.order).toEqual('1'); fixture.componentInstance.pin = false; fixture.detectChanges(); expect(drawer.pin).toBeFalsy(); expect(drawerElem.style.flexBasis).toEqual('0px'); expect(drawerElem.style.order).toEqual('0'); }); })); it('should toggle on edge swipe gesture', (done) => { let fixture: ComponentFixture<TestComponentDIComponent>; TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); expect(fixture.componentInstance.navDrawer.isOpen).toEqual(false); // timeouts are +50 on the gesture to allow the swipe to be detected and triggered after the touches: return swipe(document.body, 80, 10, 100, 250, 0); }) .then(() => { expect(fixture.componentInstance.navDrawer.isOpen) .toEqual(false, 'should ignore swipes too far away from the edge'); return swipe(document.body, 10, 10, 150, 250, 0); }) .then(() => { expect(fixture.componentInstance.navDrawer.isOpen).toEqual(true, 'Should accept edge swipe'); return swipe(document.body, 180, 10, 150, -180, 0); }) .then(() => { expect(fixture.componentInstance.navDrawer.isOpen).toEqual(false); done(); }) .catch(() => { done(); }); }, 10000); it('should toggle on edge pan gesture', (done) => { let navDrawer; let fixture: ComponentFixture<TestComponentDIComponent>; // Using bare minimum of timeouts, jasmine.DEFAULT_TIMEOUT_INTERVAL can be modified only in beforeEach TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); navDrawer = fixture.componentInstance.navDrawer; expect(fixture.componentInstance.navDrawer.isOpen).toEqual(false); const listener = navDrawer.renderer.listen(document.body, 'panmove', () => { // mid gesture expect(navDrawer.drawer.classList).toContain('panning'); expect(navDrawer.drawer.style.transform) .toMatch(/translate3d\(-2\d\dpx, 0px, 0px\)/, 'Drawer should be moving with the pan'); listener(); }); return pan(document.body, 10, 10, 150, 20, 0); }) .then(() => { expect(navDrawer.isOpen).toEqual(false, 'should ignore too short pan'); // valid pan return pan(document.body, 10, 10, 100, 200, 0); }).then(() => { expect(navDrawer.isOpen).toEqual(true, 'should open on valid pan'); // not enough distance, closing return pan(document.body, 200, 10, 100, -20, 0); }).then(() => { expect(navDrawer.isOpen).toEqual(true, 'should remain open on too short pan'); // close return pan(document.body, 250, 10, 100, -200, 0); }).then(() => { expect(navDrawer.isOpen).toEqual(false, 'should close on valid pan'); done(); }).catch(() => { done(); }); }, 10000); it('should update edge zone with mini width', waitForAsync(() => { const template = `<igx-nav-drawer [miniWidth]="drawerMiniWidth"> <ng-template igxDrawer></ng-template> <ng-template igxDrawerMini></ng-template> </igx-nav-drawer>`; let fixture: ComponentFixture<TestComponentDIComponent>; TestBed.overrideComponent(TestComponentDIComponent, { set: { template } }); // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); fixture.componentInstance.drawerMiniWidth = 68; fixture.detectChanges(); expect(fixture.componentInstance.navDrawer.maxEdgeZone) .toBe(fixture.componentInstance.drawerMiniWidth * 1.1); fixture.componentInstance.drawerMiniWidth = 80; fixture.detectChanges(); expect(fixture.componentInstance.navDrawer.maxEdgeZone) .toBe(fixture.componentInstance.drawerMiniWidth * 1.1); }).catch((reason) => Promise.reject(reason)); })); it('should update width from css or property', async () => { const template = `<igx-nav-drawer [miniWidth]="drawerMiniWidth" [width]="drawerWidth"> <ng-template igxDrawer></ng-template> <ng-template igxDrawerMini></ng-template> </igx-nav-drawer>`; TestBed.overrideComponent(TestComponentDIComponent, { set: { template } }); // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 await TestBed.compileComponents(); const fixture: ComponentFixture<TestComponentDIComponent> = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); // const comp: DebugElement = fixture.debugElement.query(By.component(IgxNavbarComponent)); // defaults: expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe(''); fixture.componentInstance.navDrawer.open(); await wait(200); expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe(''); fixture.componentInstance.drawerMiniWidth = '80px'; fixture.componentInstance.drawerWidth = '250px'; fixture.detectChanges(); await wait(200); expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe('250px'); fixture.componentInstance.navDrawer.close(); await wait(200); expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe('80px'); fixture.componentInstance.drawerWidth = '350px'; fixture.detectChanges(); fixture.componentInstance.navDrawer.open(); await wait(200); expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe('350px'); }); it('should update pin based on window width (pinThreshold)', async () => { const template = `'<igx-nav-drawer [(pin)]="pin" [pinThreshold]="pinThreshold"></igx-nav-drawer>'`; TestBed.overrideComponent(TestComponentPin, { set: { template } }); // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 await TestBed.compileComponents(); const fixture: ComponentFixture<TestComponentPin> = TestBed.createComponent(TestComponentPin); // watch for initial pin with 2-way bind expression changed errors expect(() => fixture.detectChanges()).not.toThrow(); await fixture.whenStable(); // defaults: expect(fixture.componentInstance.navDrawer.pin).toBe(false, 'Should be initially unpinned'); expect(fixture.componentInstance.pin).toBe(false, 'Parent component pin should update initially'); // manual pin override fixture.componentInstance.pin = true; fixture.detectChanges(); window.dispatchEvent(new Event('resize')); // wait for debounce await wait(200); expect(fixture.componentInstance.navDrawer.pin).toBe(false, `Shouldn't change state on resize if window width is the same`); expect(fixture.componentInstance.pin).toBe(true, 'Parent component pin remain on resize if window width is the same'); fixture.componentInstance.pin = true; fixture.detectChanges(); widthSpyOverride.and.returnValue(fixture.componentInstance.pinThreshold); window.dispatchEvent(new Event('resize')); // wait for debounce await wait(200); expect(fixture.componentInstance.navDrawer.pin).toBe(true, 'Should pin on window resize over threshold'); expect(fixture.componentInstance.pin).toBe(true, 'Parent pin update on window resize over threshold'); widthSpyOverride.and.returnValue(768); window.dispatchEvent(new Event('resize')); // wait for debounce await wait(200); expect(fixture.componentInstance.navDrawer.pin).toBe(false, 'Should un-pin on window resize below threshold'); expect(fixture.componentInstance.pin).toBe(false, 'Parent pin update on window resize below threshold'); fixture.componentInstance.pinThreshold = 500; expect(() => fixture.detectChanges()).not.toThrow(); await fixture.whenStable(); expect(fixture.componentInstance.navDrawer.pin).toBe(true, 'Should re-pin on window resize over threshold'); expect(fixture.componentInstance.pin).toBe(true, 'Parent pin update on re-pin'); }); it('should get correct window width', (done) => { const originalWidth = window.innerWidth; const platformUtil: PlatformUtil = new PlatformUtil(TestBed.inject(PLATFORM_ID)); const drawer = new IgxNavigationDrawerComponent(null, null, null, null, platformUtil); // re-enable `getWindowWidth` const widthSpy = (widthSpyOverride as jasmine.Spy).and.callThrough(); let width = widthSpy.call(drawer); expect(width).toEqual(originalWidth); (window as any).innerWidth = 0; // not that readonly in Chrome width = widthSpy.call(drawer); expect(width).toEqual(screen.width); (window as any).innerWidth = originalWidth; done(); }); it('should retain classes added in markup, fix for #6508', () => { const fix = TestBed.createComponent(TestComponent); fix.detectChanges(); expect(fix.componentInstance.navDrawer.element.classList.contains('markupClass')).toBeTruthy(); expect(fix.componentInstance.navDrawer.element.classList.contains('igx-nav-drawer')).toBeTruthy(); }); const swipe = (element, posX, posY, duration, deltaX, deltaY) => { const swipeOptions = { deltaX, deltaY, duration, pos: [posX, posY] }; return new Promise<void>(resolve => { // force touch (https://github.com/hammerjs/hammer.js/issues/1065) Simulator.setType('touch'); Simulator.gestures.swipe(element, swipeOptions, () => { resolve(); }); }); }; const pan = (element, posX, posY, duration, deltaX, deltaY) => { const swipeOptions = { deltaX, deltaY, duration, pos: [posX, posY] }; return new Promise<void>(resolve => { // force touch (https://github.com/hammerjs/hammer.js/issues/1065) Simulator.setType('touch'); Simulator.gestures.pan(element, swipeOptions, () => { resolve(); }); }); }; }); @Component({ selector: 'igx-test-cmp', template: '<igx-nav-drawer class="markupClass"></igx-nav-drawer>', standalone: true, imports: [IgxNavigationDrawerComponent] }) class TestComponent { @ViewChild(IgxNavigationDrawerComponent, { static: true }) public navDrawer: IgxNavigationDrawerComponent; } @Component({ providers: [IgxNavigationService], selector: 'igx-test-cmp', template: '<igx-nav-drawer></igx-nav-drawer>', standalone: true, imports: [IgxNavigationDrawerComponent, IgxNavDrawerTemplateDirective, IgxNavDrawerMiniTemplateDirective, NgIf] }) class TestComponentDIComponent { @ViewChild(IgxNavigationDrawerComponent, { static: true }) public navDrawer: IgxNavigationDrawerComponent; public drawerMiniWidth: string | number; public drawerWidth: string | number; } class TestComponentPin extends TestComponentDIComponent { public pin = true; public enableGestures = ''; public pinThreshold = 1024; } class TestComponentMini extends TestComponentDIComponent { public miniView = true; }