/* tslint:disable:max-classes-per-file max-file-line-count component-class-suffix */ /** * @copyright Angular ng-bootstrap team */ import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core'; import { fakeAsync, inject, TestBed, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { TooltipConfig, TooltipContainerComponent, TooltipDirective, TooltipModule } from '../../tooltip/index'; import { createGenericTestComponent } from './test/common'; @Component({ selector: 'test-onpush-cmpt', changeDetection: ChangeDetectionStrategy.OnPush, template: `` }) export class TestOnPushComponent {} @Component({selector: 'test-cmpt', template: ``}) export class TestComponent { name = 'World'; show = true; @ViewChild(TooltipDirective) tooltip: TooltipDirective; shown(): void { return; } hidden(): void { return; } } const createTestComponent = (html: string) => createGenericTestComponent(html, TestComponent); const createOnPushTestComponent = (html: string) => createGenericTestComponent(html, TestOnPushComponent); describe('tooltip-container', () => { beforeEach(() => { TestBed.configureTestingModule({imports: [TooltipModule.forRoot()]}); }); it('should render tooltip on top by default', () => { const fixture = TestBed.createComponent(TooltipContainerComponent); fixture.detectChanges(); expect(fixture.nativeElement).toHaveCssClass('tooltip'); expect(fixture.nativeElement).toHaveCssClass('tooltip-top'); expect(fixture.nativeElement.getAttribute('role')).toBe('tooltip'); }); it('should position tooltips as requested', () => { const fixture = TestBed.createComponent(TooltipContainerComponent); fixture.componentInstance.placement = 'left'; fixture.detectChanges(); expect(fixture.nativeElement).toHaveCssClass('tooltip-left'); }); }); describe('tooltip', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [TestComponent, TestOnPushComponent], imports: [TooltipModule.forRoot()] }); }); function getWindow(element: any): HTMLElement { return element.querySelector('bs-tooltip-container'); } describe('basic functionality', () => { it('should open and close a tooltip - default settings and content as string', () => { const fixture = createTestComponent(`
`); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); const defaultConfig = new TooltipConfig(); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); const windowEl = getWindow(fixture.nativeElement); expect(windowEl).toHaveCssClass('tooltip'); expect(windowEl).toHaveCssClass(`tooltip-${defaultConfig.placement}`); expect(windowEl.textContent.trim()).toBe('Great tip!'); expect(windowEl.getAttribute('role')).toBe('tooltip'); expect(windowEl.parentNode).toBe(fixture.nativeElement); directive.triggerEventHandler('mouseout', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it('should open and close a tooltip - default settings and content from a template', () => { const fixture = createTestComponent( `Hello, {{name}}!
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); const windowEl = getWindow(fixture.nativeElement); expect(windowEl).toHaveCssClass('tooltip'); expect(windowEl).toHaveCssClass('tooltip-top'); expect(windowEl.textContent.trim()).toBe('Hello, World!'); expect(windowEl.getAttribute('role')).toBe('tooltip'); expect(windowEl.parentNode).toBe(fixture.nativeElement); directive.triggerEventHandler('mouseout', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it('should not open a tooltip if content is falsy', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); const windowEl = getWindow(fixture.nativeElement); expect(windowEl).toBeNull(); }); it('should close the tooltip tooltip if content becomes falsy', () => { const fixture = createTestComponent(`
`); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); fixture.componentInstance.name = null; fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it('should allow re-opening previously closed tooltips', () => { const fixture = createTestComponent(`
`); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); directive.triggerEventHandler('mouseout', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); }); it('should not leave dangling tooltips in the DOM', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); fixture.componentInstance.show = false; fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it('should properly cleanup tooltips with manual triggers', () => { const fixture = createTestComponent(`
`); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); fixture.componentInstance.show = false; fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); describe('positioning', () => { it('should use requested position', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); const windowEl = getWindow(fixture.nativeElement); expect(windowEl).toHaveCssClass('tooltip'); expect(windowEl).toHaveCssClass('tooltip-left'); expect(windowEl.textContent.trim()).toBe('Great tip!'); }); it('should properly position tooltips when a component is using the OnPush strategy', () => { const fixture = createOnPushTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); const windowEl = getWindow(fixture.nativeElement); expect(windowEl).toHaveCssClass('tooltip'); expect(windowEl).toHaveCssClass('tooltip-left'); expect(windowEl.textContent.trim()).toBe('Great tip!'); }); it('should use auto position', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); const windowEl = getWindow(fixture.nativeElement); expect(windowEl).toHaveCssClass('tooltip'); expect(windowEl).toHaveCssClass('tooltip-auto'); expect(windowEl).toHaveCssClass('right'); expect(windowEl.textContent.trim()).toBe('Great tip!'); }); }); describe('triggers', () => { it('should support toggle triggers', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('click', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); directive.triggerEventHandler('click', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it('should non-default toggle triggers', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); directive.triggerEventHandler('click', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it('should support multiple triggers', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); directive.triggerEventHandler('click', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it('should not use default for manual triggers', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); it( 'should allow toggling for manual triggers', fakeAsync(() => { const fixture = createTestComponent(`
`); const button = fixture.nativeElement.querySelector('button'); button.click(); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); button.click(); fixture.detectChanges(); tick(150); expect(getWindow(fixture.nativeElement)).toBeNull(); }) ); it( 'should allow open / close for manual triggers', fakeAsync(() => { const fixture = createTestComponent(`
`); const buttons = fixture.nativeElement.querySelectorAll('button'); buttons[0].click(); // open fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); buttons[1].click(); // close fixture.detectChanges(); tick(150); expect(getWindow(fixture.nativeElement)).toBeNull(); }) ); it('should not throw when open called for manual triggers and open tooltip', () => { const fixture = createTestComponent(`
`); const button = fixture.nativeElement.querySelector('button'); button.click(); // open fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); button.click(); // open fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); }); it('should not throw when closed called for manual triggers and closed tooltip', () => { const fixture = createTestComponent(`
`); const button = fixture.nativeElement.querySelector('button'); button.click(); // close fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); }); }); }); describe('container', () => { it('should be appended to the element matching the selector passed to "container"', () => { const selector = 'body'; const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); expect(getWindow(document.querySelector(selector))).not.toBeNull(); }); it('should properly destroy tooltips when the "container" option is used', () => { const selector = 'body'; const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); directive.triggerEventHandler('mouseover', {}); fixture.detectChanges(); expect(getWindow(document.querySelector(selector))).not.toBeNull(); fixture.componentRef.instance.show = false; fixture.detectChanges(); expect(getWindow(document.querySelector(selector))).toBeNull(); }); }); describe('visibility', () => { it('should emit events when showing and hiding popover', () => { const fixture = createTestComponent( `
` ); const directive = fixture.debugElement.query( By.directive(TooltipDirective) ); const shownSpy = spyOn(fixture.componentInstance, 'shown'); const hiddenSpy = spyOn(fixture.componentInstance, 'hidden'); directive.triggerEventHandler('click', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); expect(shownSpy).toHaveBeenCalled(); directive.triggerEventHandler('click', {}); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); expect(hiddenSpy).toHaveBeenCalled(); }); it('should not emit close event when already closed', () => { const fixture = createTestComponent( `
` ); const shownSpy = spyOn(fixture.componentInstance, 'shown'); const hiddenSpy = spyOn(fixture.componentInstance, 'hidden'); fixture.componentInstance.tooltip.show(); fixture.detectChanges(); fixture.componentInstance.tooltip.show(); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).not.toBeNull(); expect(shownSpy).toHaveBeenCalled(); expect(shownSpy.calls.count()).toEqual(1); expect(hiddenSpy).not.toHaveBeenCalled(); }); it('should not emit open event when already opened', () => { const fixture = createTestComponent( `
` ); const shownSpy = spyOn(fixture.componentInstance, 'shown'); const hiddenSpy = spyOn(fixture.componentInstance, 'hidden'); fixture.componentInstance.tooltip.hide(); fixture.detectChanges(); expect(getWindow(fixture.nativeElement)).toBeNull(); expect(shownSpy).not.toHaveBeenCalled(); expect(hiddenSpy).not.toHaveBeenCalled(); }); it( 'should report correct visibility', fakeAsync(() => { const fixture = createTestComponent( `
` ); fixture.detectChanges(); expect(fixture.componentInstance.tooltip.isOpen).toBeFalsy(); fixture.componentInstance.tooltip.show(); fixture.detectChanges(); tick(150); expect(fixture.componentInstance.tooltip.isOpen).toBeTruthy(); fixture.componentInstance.tooltip.hide(); fixture.detectChanges(); tick(150); expect(fixture.componentInstance.tooltip.isOpen).toBeFalsy(); }) ); }); describe('Custom config', () => { let config: TooltipConfig; beforeEach(() => { TestBed.configureTestingModule({imports: [TooltipModule.forRoot()]}); TestBed.overrideComponent(TestComponent, { set: {template: `
`} }); }); beforeEach( inject([TooltipConfig], (c: TooltipConfig) => { config = c; config.placement = 'bottom'; config.triggers = 'click'; config.container = 'body'; }) ); it('should initialize inputs with provided config', () => { const fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); const tooltip = fixture.componentInstance.tooltip; expect(tooltip.placement).toBe(config.placement); expect(tooltip.triggers).toBe(config.triggers); expect(tooltip.container).toBe(config.container); }); }); describe('Custom config as provider', () => { const config = new TooltipConfig(); config.placement = 'bottom'; config.triggers = 'click'; config.container = 'body'; beforeEach(() => { TestBed.configureTestingModule({ imports: [TooltipModule.forRoot()], providers: [{provide: TooltipConfig, useValue: config}] }); }); it('should initialize inputs with provided config as provider', () => { const fixture = createTestComponent(`
`); const tooltip = fixture.componentInstance.tooltip; expect(tooltip.placement).toBe(config.placement); expect(tooltip.triggers).toBe(config.triggers); expect(tooltip.container).toBe(config.container); }); }); });