import { Component, ElementRef, Input, NgModule, TemplateRef, Type, ViewChild } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Subject } from 'rxjs'; import createSpy = jasmine.createSpy; import { NbThemeModule } from '../../theme.module'; import { NbLayoutModule } from '../layout/layout.module'; import { NbAdjustment, NbPosition } from '../cdk/overlay/overlay-position'; import { NbDynamicOverlayHandler } from '../cdk/overlay/dynamic/dynamic-overlay-handler'; import { NbOverlayContent } from '../cdk/overlay/overlay-service'; import { NbRenderableContainer } from '../cdk/overlay/overlay-container'; import { NbTrigger } from '../cdk/overlay/overlay-trigger'; import { NbPopoverDirective } from './popover.directive'; import { NbPopoverComponent } from './popover.component'; import { NbPopoverModule } from './popover.module'; import { NbOverlayConfig } from '../cdk/overlay/mapping'; @Component({ selector: 'nb-popover-component-content-test', template: 'test, {{ text }}', }) export class NbPopoverComponentContentTestComponent { text: string; } @Component({ selector: 'nb-popover-default-test', template: ` `, }) export class NbPopoverDefaultTestComponent { @ViewChild('button') button: ElementRef; @ViewChild(NbPopoverDirective) popover: NbPopoverDirective; popoverClass = ''; } @Component({ selector: 'nb-popover-bindings-test', template: ` Some Template {{ data.text }} `, }) export class NbPopoverBindingsTestComponent { @ViewChild(NbPopoverDirective) popover: NbPopoverDirective; @ViewChild('button') button: ElementRef; @ViewChild(TemplateRef) template: TemplateRef; @Input() content: any = ''; @Input() context: any = { text: 'hello world' }; @Input() trigger = NbTrigger.CLICK; @Input() position = NbPosition.TOP; @Input() adjustment = NbAdjustment.CLOCKWISE; } @Component({ selector: 'nb-popover-instance-test', template: ` Some Template `, }) export class NbPopoverInstanceTestComponent { @ViewChild(NbPopoverDirective) popover: NbPopoverDirective; @ViewChild('button') button: ElementRef; @ViewChild(TemplateRef) template: TemplateRef; } const dynamicOverlayIsShow$ = new Subject(); const dynamicOverlay = { show() {}, hide() {}, toggle() {}, destroy() {}, isShown: dynamicOverlayIsShow$, }; export class NbDynamicOverlayHandlerMock { _componentType: Type; _host: ElementRef; _context: Object = {}; _content: NbOverlayContent; _trigger: NbTrigger = NbTrigger.NOOP; _position: NbPosition = NbPosition.TOP; _adjustment: NbAdjustment = NbAdjustment.NOOP; _offset = 15; _overlayConfig: NbOverlayConfig = {}; constructor() { } host(host: ElementRef) { this._host = host; return this; } trigger(trigger: NbTrigger) { this._trigger = trigger; return this; } position(position: NbPosition) { this._position = position; return this; } offset(offset: number) { this._offset = offset; return this; } adjustment(adjustment: NbAdjustment) { this._adjustment = adjustment; return this; } componentType(componentType: Type) { this._componentType = componentType; return this; } content(content: NbOverlayContent) { this._content = content; return this; } context(context: {}) { this._context = context; return this; } overlayConfig(overlayConfig: NbOverlayConfig) { this._overlayConfig = overlayConfig; return this; } build() { return dynamicOverlay; } rebuild() { return dynamicOverlay; } connect() { } disconnect() { } destroy() { } } const TEST_COMPONENTS = [ NbPopoverDefaultTestComponent, NbPopoverBindingsTestComponent, NbPopoverInstanceTestComponent, NbPopoverComponentContentTestComponent, ]; @NgModule({ imports: [NbLayoutModule, NbPopoverModule], exports: [...TEST_COMPONENTS], declarations: [...TEST_COMPONENTS], entryComponents: [NbPopoverComponentContentTestComponent], }) class PopoverTestModule { } describe('Directive: NbPopoverDirective', () => { const overlayHandler = new NbDynamicOverlayHandlerMock(); beforeEach(waitForAsync(() => { TestBed.resetTestingModule(); TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([]), NbThemeModule.forRoot(), PopoverTestModule, ], }); })); describe('smoke ', () => { let fixture: ComponentFixture; afterEach(() => { fixture.destroy(); }); it('should render string', () => { fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); fixture.componentInstance.popover.show(); fixture.detectChanges(); const primitiveOverlay = fixture.nativeElement.querySelector('.primitive-overlay'); expect(primitiveOverlay.textContent).toContain('test'); }); it('should hide', () => { fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); fixture.componentInstance.popover.show(); fixture.detectChanges(); const openedOverlay = fixture.nativeElement.querySelector('.primitive-overlay'); expect(openedOverlay.textContent).toContain('test'); fixture.componentInstance.popover.hide(); fixture.detectChanges(); const noOverlay = fixture.nativeElement.querySelector('.primitive-overlay'); expect(noOverlay).toBeNull(); }); it('should render different content type', () => { fixture = TestBed.createComponent(NbPopoverBindingsTestComponent); fixture.detectChanges(); fixture.componentInstance.popover.show(); fixture.detectChanges(); fixture.componentInstance.content = 'new string'; fixture.detectChanges(); const stringPopover = fixture.nativeElement.querySelector('nb-popover'); expect(stringPopover.textContent).toContain('new string'); fixture.componentInstance.content = NbPopoverComponentContentTestComponent; fixture.detectChanges(); const componentPopover = fixture.nativeElement.querySelector('nb-popover-component-content-test'); expect(componentPopover.textContent).toContain('hello world'); fixture.componentInstance.content = fixture.componentInstance.template; fixture.detectChanges(); const templatePopover = fixture.nativeElement.querySelector('nb-popover'); expect(templatePopover.textContent).toContain('hello world'); }); it('should emit show state change event when shows up', () => { fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); const popover: NbPopoverDirective = fixture.componentInstance.popover; const stateChangeSpy = createSpy('stateChangeSpy'); popover.nbPopoverShowStateChange.subscribe(stateChangeSpy); popover.show(); fixture.detectChanges(); expect(stateChangeSpy).toHaveBeenCalledTimes(1); expect(stateChangeSpy).toHaveBeenCalledWith(jasmine.objectContaining({ isShown: true })); }); it('should emit show state change event when hides', () => { fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); const popover: NbPopoverDirective = fixture.componentInstance.popover; popover.show(); fixture.detectChanges(); const stateChangeSpy = createSpy('stateChangeSpy'); popover.nbPopoverShowStateChange.subscribe(stateChangeSpy); popover.hide(); fixture.detectChanges(); expect(stateChangeSpy).toHaveBeenCalledTimes(1); expect(stateChangeSpy).toHaveBeenCalledWith(jasmine.objectContaining({ isShown: false })); }); it('should set isShown to false when hidden', () => { fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); const popover: NbPopoverDirective = fixture.componentInstance.popover; expect(popover.isShown).toEqual(false); }); it('should set isShown to true when shown', () => { fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); const popover: NbPopoverDirective = fixture.componentInstance.popover; popover.show(); fixture.detectChanges(); expect(popover.isShown).toEqual(true); }); }); describe('mocked services', () => { beforeEach(waitForAsync(() => { TestBed.resetTestingModule(); TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([]), NbThemeModule.forRoot(), PopoverTestModule, ], }) .overrideDirective(NbPopoverDirective, { set: { providers: [ { provide: NbDynamicOverlayHandler, useValue: overlayHandler }, ], }, }); })); describe('default popover', () => { let fixture: ComponentFixture; afterEach(() => { fixture.destroy(); }); it('should build', () => { const componentSpy = spyOn(overlayHandler, 'componentType').and.callThrough(); const hostSpy = spyOn(overlayHandler, 'host').and.callThrough(); const positionSpy = spyOn(overlayHandler, 'position').and.callThrough(); const triggerSpy = spyOn(overlayHandler, 'trigger').and.callThrough(); const adjustmentSpy = spyOn(overlayHandler, 'adjustment').and.callThrough(); const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); const contextSpy = spyOn(overlayHandler, 'context').and.callThrough(); const buildSpy = spyOn(overlayHandler, 'build').and.callThrough(); const rebuildSpy = spyOn(overlayHandler, 'rebuild').and.callThrough(); fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); expect(componentSpy).toHaveBeenCalledTimes(1); expect(componentSpy).toHaveBeenCalledWith(NbPopoverComponent); expect(hostSpy).toHaveBeenCalledWith(fixture.componentInstance.button); expect(hostSpy).toHaveBeenCalledTimes(1); expect(positionSpy).toHaveBeenCalledTimes(2); expect(positionSpy).toHaveBeenCalledWith(NbPosition.TOP); expect(triggerSpy).toHaveBeenCalledTimes(2); expect(triggerSpy).toHaveBeenCalledWith(NbTrigger.CLICK); expect(adjustmentSpy).toHaveBeenCalledTimes(2); expect(adjustmentSpy).toHaveBeenCalledWith(NbAdjustment.CLOCKWISE); expect(contentSpy).toHaveBeenCalledTimes(2); expect(contentSpy).toHaveBeenCalledWith('test'); expect(contextSpy).toHaveBeenCalledTimes(2); expect(contextSpy).toHaveBeenCalledWith({}); expect(buildSpy).toHaveBeenCalledTimes(1); expect(rebuildSpy).toHaveBeenCalledTimes(1); }); it('should show/hide/toggle', () => { fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.detectChanges(); const showSpy = spyOn(dynamicOverlay, 'show').and.callThrough(); const hideSpy = spyOn(dynamicOverlay, 'hide').and.callThrough(); const toggleSpy = spyOn(dynamicOverlay, 'toggle').and.callThrough(); fixture.componentInstance.popover.show(); fixture.detectChanges(); expect(showSpy).toHaveBeenCalledTimes(1); expect(hideSpy).toHaveBeenCalledTimes(0); expect(toggleSpy).toHaveBeenCalledTimes(0); fixture.componentInstance.popover.hide(); fixture.detectChanges(); expect(showSpy).toHaveBeenCalledTimes(1); expect(hideSpy).toHaveBeenCalledTimes(1); expect(toggleSpy).toHaveBeenCalledTimes(0); fixture.componentInstance.popover.toggle(); fixture.detectChanges(); expect(showSpy).toHaveBeenCalledTimes(1); expect(hideSpy).toHaveBeenCalledTimes(1); expect(toggleSpy).toHaveBeenCalledTimes(1); }); it('should set overlay config', () => { const popoverClass = 'custom-popover-class'; const overlayConfigSpy = spyOn(overlayHandler, 'overlayConfig').and.callThrough(); fixture = TestBed.createComponent(NbPopoverDefaultTestComponent); fixture.componentInstance.popoverClass = popoverClass; fixture.detectChanges(); expect(overlayConfigSpy).toHaveBeenCalledWith(jasmine.objectContaining({ panelClass: popoverClass })); }); }); describe('binding popover', () => { let fixture: ComponentFixture; afterEach(() => { fixture.destroy(); }); it('should rebuild', () => { const componentSpy = spyOn(overlayHandler, 'componentType').and.callThrough(); const hostSpy = spyOn(overlayHandler, 'host').and.callThrough(); const positionSpy = spyOn(overlayHandler, 'position').and.callThrough(); const triggerSpy = spyOn(overlayHandler, 'trigger').and.callThrough(); const adjustmentSpy = spyOn(overlayHandler, 'adjustment').and.callThrough(); const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); const contextSpy = spyOn(overlayHandler, 'context').and.callThrough(); const buildSpy = spyOn(overlayHandler, 'build').and.callThrough(); const rebuildSpy = spyOn(overlayHandler, 'rebuild').and.callThrough(); fixture = TestBed.createComponent(NbPopoverBindingsTestComponent); fixture.detectChanges(); fixture.componentInstance.adjustment = NbAdjustment.HORIZONTAL; fixture.componentInstance.trigger = NbTrigger.HINT; fixture.componentInstance.content = 'new string'; fixture.componentInstance.context = { context: 'new' }; fixture.componentInstance.position = NbPosition.LEFT; fixture.detectChanges(); expect(componentSpy).toHaveBeenCalledTimes(1); expect(componentSpy).toHaveBeenCalledWith(NbPopoverComponent); expect(hostSpy).toHaveBeenCalledWith(fixture.componentInstance.button); expect(hostSpy).toHaveBeenCalledTimes(1); expect(positionSpy).toHaveBeenCalledTimes(3); expect(positionSpy).toHaveBeenCalledWith(NbPosition.LEFT); expect(triggerSpy).toHaveBeenCalledTimes(3); expect(triggerSpy).toHaveBeenCalledWith(NbTrigger.HINT); expect(adjustmentSpy).toHaveBeenCalledTimes(3); expect(adjustmentSpy).toHaveBeenCalledWith(NbAdjustment.HORIZONTAL); expect(contentSpy).toHaveBeenCalledTimes(3); expect(contentSpy).toHaveBeenCalledWith('new string'); expect(contextSpy).toHaveBeenCalledTimes(3); expect(contextSpy).toHaveBeenCalledWith({ context: 'new' }); expect(buildSpy).toHaveBeenCalledTimes(1); expect(rebuildSpy).toHaveBeenCalledTimes(2); }); it('should accept different content type', () => { const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); fixture = TestBed.createComponent(NbPopoverBindingsTestComponent); fixture.detectChanges(); fixture.componentInstance.content = 'new string'; fixture.detectChanges(); expect(contentSpy).toHaveBeenCalledTimes(3); expect(contentSpy).toHaveBeenCalledWith('new string'); fixture.componentInstance.content = NbPopoverComponentContentTestComponent; fixture.detectChanges(); expect(contentSpy).toHaveBeenCalledTimes(4); expect(contentSpy).toHaveBeenCalledWith(NbPopoverComponentContentTestComponent); fixture.componentInstance.content = fixture.componentInstance.template; fixture.detectChanges(); expect(contentSpy).toHaveBeenCalledTimes(5); expect(contentSpy).toHaveBeenCalledWith(fixture.componentInstance.template); }); }); describe('instance popover', () => { let fixture: ComponentFixture; afterEach(() => { fixture.destroy(); }); it('should rebuild', () => { const componentSpy = spyOn(overlayHandler, 'componentType').and.callThrough(); const hostSpy = spyOn(overlayHandler, 'host').and.callThrough(); const positionSpy = spyOn(overlayHandler, 'position').and.callThrough(); const triggerSpy = spyOn(overlayHandler, 'trigger').and.callThrough(); const adjustmentSpy = spyOn(overlayHandler, 'adjustment').and.callThrough(); const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); const contextSpy = spyOn(overlayHandler, 'context').and.callThrough(); const buildSpy = spyOn(overlayHandler, 'build').and.callThrough(); const rebuildSpy = spyOn(overlayHandler, 'rebuild').and.callThrough(); fixture = TestBed.createComponent(NbPopoverInstanceTestComponent); fixture.detectChanges(); fixture.componentInstance.popover.adjustment = NbAdjustment.HORIZONTAL; fixture.componentInstance.popover.trigger = NbTrigger.HINT; fixture.componentInstance.popover.content = 'new string'; fixture.componentInstance.popover.context = { context: 'new' }; fixture.componentInstance.popover.position = NbPosition.LEFT; fixture.componentInstance.popover.rebuild(); expect(componentSpy).toHaveBeenCalledTimes(1); expect(componentSpy).toHaveBeenCalledWith(NbPopoverComponent); expect(hostSpy).toHaveBeenCalledWith(fixture.componentInstance.button); expect(hostSpy).toHaveBeenCalledTimes(1); expect(positionSpy).toHaveBeenCalledTimes(3); expect(positionSpy).toHaveBeenCalledWith(NbPosition.LEFT); expect(triggerSpy).toHaveBeenCalledTimes(3); expect(triggerSpy).toHaveBeenCalledWith(NbTrigger.HINT); expect(adjustmentSpy).toHaveBeenCalledTimes(3); expect(adjustmentSpy).toHaveBeenCalledWith(NbAdjustment.HORIZONTAL); expect(contentSpy).toHaveBeenCalledTimes(3); expect(contentSpy).toHaveBeenCalledWith('new string'); expect(contextSpy).toHaveBeenCalledTimes(3); expect(contextSpy).toHaveBeenCalledWith({ context: 'new' }); expect(buildSpy).toHaveBeenCalledTimes(1); expect(rebuildSpy).toHaveBeenCalledTimes(2); }); it('should accept different content type', () => { const contentSpy = spyOn(overlayHandler, 'content').and.callThrough(); fixture = TestBed.createComponent(NbPopoverInstanceTestComponent); fixture.detectChanges(); fixture.componentInstance.popover.content = 'new string'; fixture.componentInstance.popover.rebuild(); expect(contentSpy).toHaveBeenCalledTimes(3); expect(contentSpy).toHaveBeenCalledWith('new string'); fixture.componentInstance.popover.content = NbPopoverComponentContentTestComponent; fixture.componentInstance.popover.rebuild(); expect(contentSpy).toHaveBeenCalledTimes(4); expect(contentSpy).toHaveBeenCalledWith(NbPopoverComponentContentTestComponent); fixture.componentInstance.popover.content = fixture.componentInstance.template; fixture.componentInstance.popover.rebuild(); expect(contentSpy).toHaveBeenCalledTimes(5); expect(contentSpy).toHaveBeenCalledWith(fixture.componentInstance.template); }); }); }); });