cd2d8a6a0aa5f2d4439ce81047ca1c7a3c3527ce
[sdc/sdc-workflow-designer.git] /
1 import {TestBed, ComponentFixture, inject} from '@angular/core/testing';
2 import {createGenericTestComponent} from '../test/common';
3
4 import {By} from '@angular/platform-browser';
5 import {Component, ViewChild, ChangeDetectionStrategy} from '@angular/core';
6
7 import {PlxTooltipModule} from './plx-tooltip.module';
8 import {PlxTooltipWindow, PlxTooltip} from './plx-tooltip';
9 import {PlxTooltipConfig} from './plx-tooltip-config';
10
11 const createTestComponent =
12     (html: string) => <ComponentFixture<TestComponent>>createGenericTestComponent(html, TestComponent);
13
14 const createOnPushTestComponent =
15     (html: string) => <ComponentFixture<TestOnPushComponent>>createGenericTestComponent(html, TestOnPushComponent);
16
17 describe('plx-tooltip-window', () => {
18   beforeEach(() => { TestBed.configureTestingModule({imports: [PlxTooltipModule.forRoot()]}); });
19
20   it('should render tooltip on top by default', () => {
21     const fixture = TestBed.createComponent(PlxTooltipWindow);
22     fixture.detectChanges();
23
24     expect(fixture.nativeElement).toHaveCssClass('tooltip');
25     expect(fixture.nativeElement).toHaveCssClass('tooltip-top');
26     expect(fixture.nativeElement.getAttribute('role')).toBe('tooltip');
27   });
28
29   it('should position tooltips as requested', () => {
30     const fixture = TestBed.createComponent(PlxTooltipWindow);
31     fixture.componentInstance.placement = 'left';
32     fixture.detectChanges();
33     expect(fixture.nativeElement).toHaveCssClass('tooltip-left');
34   });
35 });
36
37 describe('plx-tooltip', () => {
38
39   beforeEach(() => {
40     TestBed.configureTestingModule(
41         {declarations: [TestComponent, TestOnPushComponent], imports: [PlxTooltipModule.forRoot()]});
42   });
43
44   function getWindow(element) { return element.querySelector('plx-tooltip-window'); }
45
46   describe('basic functionality', () => {
47
48     it('should open and close a tooltip - default settings and content as string', () => {
49       const fixture = createTestComponent(`<div plxTooltip="Great tip!"></div>`);
50       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
51       const defaultConfig = new PlxTooltipConfig();
52
53       directive.triggerEventHandler('mouseenter', {});
54       fixture.detectChanges();
55       const windowEl = getWindow(fixture.nativeElement);
56
57       expect(windowEl).toHaveCssClass('tooltip');
58       expect(windowEl).toHaveCssClass(`tooltip-${defaultConfig.placement}`);
59       expect(windowEl.textContent.trim()).toBe('Great tip!');
60       expect(windowEl.getAttribute('role')).toBe('tooltip');
61       expect(windowEl.getAttribute('id')).toBe('plx-tooltip-0');
62       expect(windowEl.parentNode).toBe(fixture.nativeElement);
63       expect(directive.nativeElement.getAttribute('aria-describedby')).toBe('plx-tooltip-0');
64
65       directive.triggerEventHandler('mouseleave', {});
66       fixture.detectChanges();
67       expect(getWindow(fixture.nativeElement)).toBeNull();
68       expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
69     });
70
71     it('should open and close a tooltip - default settings and content from a template', () => {
72       const fixture = createTestComponent(`<template #t>Hello, {{name}}!</template><div [plxTooltip]="t"></div>`);
73       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
74
75       directive.triggerEventHandler('mouseenter', {});
76       fixture.detectChanges();
77       const windowEl = getWindow(fixture.nativeElement);
78
79       expect(windowEl).toHaveCssClass('tooltip');
80       expect(windowEl).toHaveCssClass('tooltip-top');
81       expect(windowEl.textContent.trim()).toBe('Hello, World!');
82       expect(windowEl.getAttribute('role')).toBe('tooltip');
83       expect(windowEl.getAttribute('id')).toBe('plx-tooltip-1');
84       expect(windowEl.parentNode).toBe(fixture.nativeElement);
85       expect(directive.nativeElement.getAttribute('aria-describedby')).toBe('plx-tooltip-1');
86
87       directive.triggerEventHandler('mouseleave', {});
88       fixture.detectChanges();
89       expect(getWindow(fixture.nativeElement)).toBeNull();
90       expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
91     });
92
93     it('should open and close a tooltip - default settings, content from a template and context supplied', () => {
94       const fixture = createTestComponent(`<template #t let-name="name">Hello, {{name}}!</template><div [plxTooltip]="t"></div>`);
95       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
96
97       directive.context.tooltip.open({name: 'John'});
98       fixture.detectChanges();
99       const windowEl = getWindow(fixture.nativeElement);
100
101       expect(windowEl).toHaveCssClass('tooltip');
102       expect(windowEl).toHaveCssClass('tooltip-top');
103       expect(windowEl.textContent.trim()).toBe('Hello, John!');
104       expect(windowEl.getAttribute('role')).toBe('tooltip');
105       expect(windowEl.getAttribute('id')).toBe('plx-tooltip-2');
106       expect(windowEl.parentNode).toBe(fixture.nativeElement);
107       expect(directive.nativeElement.getAttribute('aria-describedby')).toBe('plx-tooltip-2');
108
109       directive.triggerEventHandler('mouseleave', {});
110       fixture.detectChanges();
111       expect(getWindow(fixture.nativeElement)).toBeNull();
112       expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
113     });
114
115     it('should not open a tooltip if content is falsy', () => {
116       const fixture = createTestComponent(`<div [plxTooltip]="notExisting"></div>`);
117       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
118
119       directive.triggerEventHandler('mouseenter', {});
120       fixture.detectChanges();
121       const windowEl = getWindow(fixture.nativeElement);
122
123       expect(windowEl).toBeNull();
124     });
125
126     it('should close the tooltip tooltip if content becomes falsy', () => {
127       const fixture = createTestComponent(`<div [plxTooltip]="name"></div>`);
128       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
129
130       directive.triggerEventHandler('mouseenter', {});
131       fixture.detectChanges();
132       expect(getWindow(fixture.nativeElement)).not.toBeNull();
133
134       fixture.componentInstance.name = null;
135       fixture.detectChanges();
136       expect(getWindow(fixture.nativeElement)).toBeNull();
137     });
138
139     it('should allow re-opening previously closed tooltips', () => {
140       const fixture = createTestComponent(`<div plxTooltip="Great tip!"></div>`);
141       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
142
143       directive.triggerEventHandler('mouseenter', {});
144       fixture.detectChanges();
145       expect(getWindow(fixture.nativeElement)).not.toBeNull();
146
147       directive.triggerEventHandler('mouseleave', {});
148       fixture.detectChanges();
149       expect(getWindow(fixture.nativeElement)).toBeNull();
150
151       directive.triggerEventHandler('mouseenter', {});
152       fixture.detectChanges();
153       expect(getWindow(fixture.nativeElement)).not.toBeNull();
154     });
155
156     it('should not leave dangling tooltips in the DOM', () => {
157       const fixture = createTestComponent(`<template [ngIf]="show"><div plxTooltip="Great tip!"></div></template>`);
158       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
159
160       directive.triggerEventHandler('mouseenter', {});
161       fixture.detectChanges();
162       expect(getWindow(fixture.nativeElement)).not.toBeNull();
163
164       fixture.componentInstance.show = false;
165       fixture.detectChanges();
166       expect(getWindow(fixture.nativeElement)).toBeNull();
167     });
168
169     it('should properly cleanup tooltips with manual triggers', () => {
170       const fixture = createTestComponent(`
171             <template [ngIf]="show">
172               <div plxTooltip="Great tip!" triggers="manual" #t="plxTooltip" (mouseenter)="t.open()"></div>
173             </template>`);
174       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
175
176       directive.triggerEventHandler('mouseenter', {});
177       fixture.detectChanges();
178       expect(getWindow(fixture.nativeElement)).not.toBeNull();
179
180       fixture.componentInstance.show = false;
181       fixture.detectChanges();
182       expect(getWindow(fixture.nativeElement)).toBeNull();
183     });
184
185     describe('positioning', () => {
186
187       it('should use requested position', () => {
188         const fixture = createTestComponent(`<div plxTooltip="Great tip!" placement="left"></div>`);
189         const directive = fixture.debugElement.query(By.directive(PlxTooltip));
190
191         directive.triggerEventHandler('mouseenter', {});
192         fixture.detectChanges();
193         const windowEl = getWindow(fixture.nativeElement);
194
195         expect(windowEl).toHaveCssClass('tooltip');
196         expect(windowEl).toHaveCssClass('tooltip-left');
197         expect(windowEl.textContent.trim()).toBe('Great tip!');
198       });
199
200       it('should properly position tooltips when a component is using the OnPush strategy', () => {
201         const fixture = createOnPushTestComponent(`<div plxTooltip="Great tip!" placement="left"></div>`);
202         const directive = fixture.debugElement.query(By.directive(PlxTooltip));
203
204         directive.triggerEventHandler('mouseenter', {});
205         fixture.detectChanges();
206         const windowEl = getWindow(fixture.nativeElement);
207
208         expect(windowEl).toHaveCssClass('tooltip');
209         expect(windowEl).toHaveCssClass('tooltip-left');
210         expect(windowEl.textContent.trim()).toBe('Great tip!');
211       });
212     });
213
214     describe('triggers', () => {
215
216       it('should support toggle triggers', () => {
217         const fixture = createTestComponent(`<div plxTooltip="Great tip!" triggers="click"></div>`);
218         const directive = fixture.debugElement.query(By.directive(PlxTooltip));
219
220         directive.triggerEventHandler('click', {});
221         fixture.detectChanges();
222         expect(getWindow(fixture.nativeElement)).not.toBeNull();
223
224         directive.triggerEventHandler('click', {});
225         fixture.detectChanges();
226         expect(getWindow(fixture.nativeElement)).toBeNull();
227       });
228
229       it('should non-default toggle triggers', () => {
230         const fixture = createTestComponent(`<div plxTooltip="Great tip!" triggers="mouseenter:click"></div>`);
231         const directive = fixture.debugElement.query(By.directive(PlxTooltip));
232
233         directive.triggerEventHandler('mouseenter', {});
234         fixture.detectChanges();
235         expect(getWindow(fixture.nativeElement)).not.toBeNull();
236
237         directive.triggerEventHandler('click', {});
238         fixture.detectChanges();
239         expect(getWindow(fixture.nativeElement)).toBeNull();
240       });
241
242       it('should support multiple triggers', () => {
243         const fixture =
244             createTestComponent(`<div plxTooltip="Great tip!" triggers="mouseenter:mouseleave click"></div>`);
245         const directive = fixture.debugElement.query(By.directive(PlxTooltip));
246
247         directive.triggerEventHandler('mouseenter', {});
248         fixture.detectChanges();
249         expect(getWindow(fixture.nativeElement)).not.toBeNull();
250
251         directive.triggerEventHandler('click', {});
252         fixture.detectChanges();
253         expect(getWindow(fixture.nativeElement)).toBeNull();
254       });
255
256       it('should not use default for manual triggers', () => {
257         const fixture = createTestComponent(`<div plxTooltip="Great tip!" triggers="manual"></div>`);
258         const directive = fixture.debugElement.query(By.directive(PlxTooltip));
259
260         directive.triggerEventHandler('mouseenter', {});
261         fixture.detectChanges();
262         expect(getWindow(fixture.nativeElement)).toBeNull();
263       });
264
265       it('should allow toggling for manual triggers', () => {
266         const fixture = createTestComponent(`
267                 <div plxTooltip="Great tip!" triggers="manual" #t="plxTooltip"></div>
268                 <button (click)="t.toggle()">T</button>`);
269         const button = fixture.nativeElement.querySelector('button');
270
271         button.click();
272         fixture.detectChanges();
273         expect(getWindow(fixture.nativeElement)).not.toBeNull();
274
275         button.click();
276         fixture.detectChanges();
277         expect(getWindow(fixture.nativeElement)).toBeNull();
278       });
279
280       it('should allow open / close for manual triggers', () => {
281         const fixture = createTestComponent(`
282                 <div plxTooltip="Great tip!" triggers="manual" #t="plxTooltip"></div>
283                 <button (click)="t.open()">O</button>
284                 <button (click)="t.close()">C</button>`);
285
286         const buttons = fixture.nativeElement.querySelectorAll('button');
287
288         buttons[0].click();  // open
289         fixture.detectChanges();
290         expect(getWindow(fixture.nativeElement)).not.toBeNull();
291
292         buttons[1].click();  // close
293         fixture.detectChanges();
294         expect(getWindow(fixture.nativeElement)).toBeNull();
295       });
296
297       it('should not throw when open called for manual triggers and open tooltip', () => {
298         const fixture = createTestComponent(`
299                 <div plxTooltip="Great tip!" triggers="manual" #t="plxTooltip"></div>
300                 <button (click)="t.open()">O</button>`);
301         const button = fixture.nativeElement.querySelector('button');
302
303         button.click();  // open
304         fixture.detectChanges();
305         expect(getWindow(fixture.nativeElement)).not.toBeNull();
306
307         button.click();  // open
308         fixture.detectChanges();
309         expect(getWindow(fixture.nativeElement)).not.toBeNull();
310       });
311
312       it('should not throw when closed called for manual triggers and closed tooltip', () => {
313         const fixture = createTestComponent(`
314                 <div plxTooltip="Great tip!" triggers="manual" #t="plxTooltip"></div>
315                 <button (click)="t.close()">C</button>`);
316
317         const button = fixture.nativeElement.querySelector('button');
318
319         button.click();  // close
320         fixture.detectChanges();
321         expect(getWindow(fixture.nativeElement)).toBeNull();
322       });
323     });
324   });
325
326   describe('container', () => {
327
328     it('should be appended to the element matching the selector passed to "container"', () => {
329       const selector = 'body';
330       const fixture = createTestComponent(`<div plxTooltip="Great tip!" container="` + selector + `"></div>`);
331       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
332
333       directive.triggerEventHandler('mouseenter', {});
334       fixture.detectChanges();
335       expect(getWindow(fixture.nativeElement)).toBeNull();
336       expect(getWindow(document.querySelector(selector))).not.toBeNull();
337     });
338
339     it('should properly destroy tooltips when the "container" option is used', () => {
340       const selector = 'body';
341       const fixture =
342           createTestComponent(`<div *ngIf="show" plxTooltip="Great tip!" container="` + selector + `"></div>`);
343       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
344
345       directive.triggerEventHandler('mouseenter', {});
346       fixture.detectChanges();
347
348       expect(getWindow(document.querySelector(selector))).not.toBeNull();
349       fixture.componentRef.instance.show = false;
350       fixture.detectChanges();
351       expect(getWindow(document.querySelector(selector))).toBeNull();
352     });
353   });
354
355   describe('visibility', () => {
356     it('should emit events when showing and hiding popover', () => {
357       const fixture = createTestComponent(
358           `<div plxTooltip="Great tip!" triggers="click" (shown)="shown()" (hidden)="hidden()"></div>`);
359       const directive = fixture.debugElement.query(By.directive(PlxTooltip));
360
361       let shownSpy = spyOn(fixture.componentInstance, 'shown');
362       let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');
363
364       directive.triggerEventHandler('click', {});
365       fixture.detectChanges();
366       expect(getWindow(fixture.nativeElement)).not.toBeNull();
367       expect(shownSpy).toHaveBeenCalled();
368
369       directive.triggerEventHandler('click', {});
370       fixture.detectChanges();
371       expect(getWindow(fixture.nativeElement)).toBeNull();
372       expect(hiddenSpy).toHaveBeenCalled();
373     });
374
375     it('should not emit close event when already closed', () => {
376       const fixture = createTestComponent(
377           `<div plxTooltip="Great tip!" triggers="manual" (shown)="shown()" (hidden)="hidden()"></div>`);
378
379       let shownSpy = spyOn(fixture.componentInstance, 'shown');
380       let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');
381
382       fixture.componentInstance.tooltip.open();
383       fixture.detectChanges();
384
385       fixture.componentInstance.tooltip.open();
386       fixture.detectChanges();
387
388       expect(getWindow(fixture.nativeElement)).not.toBeNull();
389       expect(shownSpy).toHaveBeenCalled();
390       expect(shownSpy.calls.count()).toEqual(1);
391       expect(hiddenSpy).not.toHaveBeenCalled();
392     });
393
394     it('should not emit open event when already opened', () => {
395       const fixture = createTestComponent(
396           `<div plxTooltip="Great tip!" triggers="manual" (shown)="shown()" (hidden)="hidden()"></div>`);
397
398       let shownSpy = spyOn(fixture.componentInstance, 'shown');
399       let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');
400
401       fixture.componentInstance.tooltip.close();
402       fixture.detectChanges();
403       expect(getWindow(fixture.nativeElement)).toBeNull();
404       expect(shownSpy).not.toHaveBeenCalled();
405       expect(hiddenSpy).toHaveBeenCalled();
406     });
407
408     it('should report correct visibility', () => {
409       const fixture = createTestComponent(`<div plxTooltip="Great tip!" triggers="manual"></div>`);
410       fixture.detectChanges();
411
412       expect(fixture.componentInstance.tooltip.isOpen()).toBeFalsy();
413
414       fixture.componentInstance.tooltip.open();
415       fixture.detectChanges();
416       expect(fixture.componentInstance.tooltip.isOpen()).toBeTruthy();
417
418       fixture.componentInstance.tooltip.close();
419       fixture.detectChanges();
420       expect(fixture.componentInstance.tooltip.isOpen()).toBeFalsy();
421     });
422   });
423
424   describe('Custom config', () => {
425     let config: PlxTooltipConfig;
426
427     beforeEach(() => {
428       TestBed.configureTestingModule({imports: [PlxTooltipModule.forRoot()]});
429       TestBed.overrideComponent(TestComponent, {set: {template: `<div plxTooltip="Great tip!"></div>`}});
430     });
431
432     beforeEach(inject([PlxTooltipConfig], (c: PlxTooltipConfig) => {
433       config = c;
434       config.placement = 'bottom';
435       config.triggers = 'click';
436       config.container = 'body';
437     }));
438
439     it('should initialize inputs with provided config', () => {
440       const fixture = TestBed.createComponent(TestComponent);
441       fixture.detectChanges();
442       const tooltip = fixture.componentInstance.tooltip;
443
444       expect(tooltip.placement).toBe(config.placement);
445       expect(tooltip.triggers).toBe(config.triggers);
446       expect(tooltip.container).toBe(config.container);
447     });
448   });
449
450   describe('Custom config as provider', () => {
451     let config = new PlxTooltipConfig();
452     config.placement = 'bottom';
453     config.triggers = 'click';
454     config.container = 'body';
455
456     beforeEach(() => {
457       TestBed.configureTestingModule(
458           {imports: [PlxTooltipModule.forRoot()], providers: [{provide: PlxTooltipConfig, useValue: config}]});
459     });
460
461     it('should initialize inputs with provided config as provider', () => {
462       const fixture = createTestComponent(`<div plxTooltip="Great tip!"></div>`);
463       const tooltip = fixture.componentInstance.tooltip;
464
465       expect(tooltip.placement).toBe(config.placement);
466       expect(tooltip.triggers).toBe(config.triggers);
467       expect(tooltip.container).toBe(config.container);
468     });
469   });
470 });
471
472 @Component({selector: 'test-cmpt', template: ``})
473 export class TestComponent {
474   name = 'World';
475   show = true;
476
477   @ViewChild(PlxTooltip) tooltip: PlxTooltip;
478
479   shown() {}
480   hidden() {}
481 }
482
483 @Component({selector: 'test-onpush-cmpt', changeDetection: ChangeDetectionStrategy.OnPush, template: ``})
484 export class TestOnPushComponent {
485 }