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
text/typescript
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();
});
});
};
});
({
selector: 'igx-test-cmp',
template: '<igx-nav-drawer class="markupClass"></igx-nav-drawer>',
standalone: true,
imports: [IgxNavigationDrawerComponent]
})
class TestComponent {
(IgxNavigationDrawerComponent, { static: true }) public navDrawer: IgxNavigationDrawerComponent;
}
({
providers: [IgxNavigationService],
selector: 'igx-test-cmp',
template: '<igx-nav-drawer></igx-nav-drawer>',
standalone: true,
imports: [IgxNavigationDrawerComponent, IgxNavDrawerTemplateDirective, IgxNavDrawerMiniTemplateDirective, NgIf]
})
class TestComponentDIComponent {
(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;
}