diff --git a/src/app/shared/context-help-wrapper/context-help-wrapper.component.spec.ts b/src/app/shared/context-help-wrapper/context-help-wrapper.component.spec.ts index aee7a0c696..a803f8ce41 100644 --- a/src/app/shared/context-help-wrapper/context-help-wrapper.component.spec.ts +++ b/src/app/shared/context-help-wrapper/context-help-wrapper.component.spec.ts @@ -1,21 +1,60 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { of as observableOf, Observable, BehaviorSubject } from 'rxjs'; import { ContextHelpWrapperComponent } from './context-help-wrapper.component'; +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { ContextHelpService } from '../context-help.service'; +import { ContextHelp } from '../context-help.model'; describe('ContextHelpWrapperComponent', () => { let component: ContextHelpWrapperComponent; let fixture: ComponentFixture; + let translateService: any; + let contextHelpService: any; + let getContextHelp$: BehaviorSubject; + let shouldShowIcons$: BehaviorSubject; - beforeEach(async () => { - await TestBed.configureTestingModule({ + const messages = { + lorem: 'lorem ipsum dolor sit amet', + linkTest: 'This is text, [this](https://dspace.lyrasis.org) is a link, and [so is this](https://google.com)' + }; + function makeWrappedElement(): HTMLElement { + let el: HTMLElement = document.createElement('div') + el.innerHTML = 'example element'; + return el; + } + + beforeEach(waitForAsync( () => { + translateService = jasmine.createSpyObj('translateService', ['get']); + translateService.get.and.callFake((content) => messages[content]); + + contextHelpService = jasmine.createSpyObj('contextHelpService', [ + 'shouldShowIcons$', + 'getContextHelp$', + 'add' + ]) + TestBed.configureTestingModule({ + imports: [ NgbTooltipModule ], + providers: [ + { provide: TranslateService, useValue: translateService }, + { provide: ContextHelpService, useValue: contextHelpService }, + ], declarations: [ ContextHelpWrapperComponent ] - }) - .compileComponents(); - }); + }).compileComponents(); + })); beforeEach(() => { + contextHelpService.getContextHelp$.and.returnValue(getContextHelp$); + contextHelpService.shouldShowIcons$.and.returnValue(shouldShowIcons$); + getContextHelp$.next({ + id: 'example-id', + isTooltipVisible: false + }); + shouldShowIcons$.next(false); + fixture = TestBed.createComponent(ContextHelpWrapperComponent); component = fixture.componentInstance; + component.templateRef fixture.detectChanges(); }); diff --git a/src/app/shared/context-help-wrapper/context-help-wrapper.component.ts b/src/app/shared/context-help-wrapper/context-help-wrapper.component.ts index 68f2ac0002..41f63877cd 100644 --- a/src/app/shared/context-help-wrapper/context-help-wrapper.component.ts +++ b/src/app/shared/context-help-wrapper/context-help-wrapper.component.ts @@ -33,13 +33,13 @@ export class ContextHelpWrapperComponent implements OnInit, OnDestroy { /** * Indicate where the tooltip should show up, relative to the info icon. */ - @Input() tooltipPlacement?: PlacementArray; + @Input() tooltipPlacement?: PlacementArray = []; /** * Indicate whether the info icon should appear to the left or to * the right of the wrapped element. */ - @Input() iconPlacement?: PlacementDir; + @Input() iconPlacement?: PlacementDir = 'left'; /** * If true, don't process text to render links. diff --git a/src/app/shared/context-help.directive.spec.ts b/src/app/shared/context-help.directive.spec.ts index 35770b6038..cbe1fd94b7 100644 --- a/src/app/shared/context-help.directive.spec.ts +++ b/src/app/shared/context-help.directive.spec.ts @@ -1,65 +1,145 @@ import { Component, DebugElement, Input } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { of as observableOf, Observable } from 'rxjs'; +import { ComponentFixture, TestBed, getTestBed, waitForAsync } from '@angular/core/testing'; +import { of as observableOf, Observable, BehaviorSubject } from 'rxjs'; import { ContextHelpDirective, ContextHelpDirectiveInput } from './context-help.directive'; import { TranslateService } from '@ngx-translate/core'; import { ContextHelpWrapperComponent } from './context-help-wrapper/context-help-wrapper.component'; import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; +import { ContextHelpService } from './context-help.service'; +import { ContextHelp } from './context-help.model'; +import { before } from 'lodash'; @Component({ template: `
some text
` }) class TestComponent { @Input() content = ''; + @Input() id = ''; contextHelpParams(): ContextHelpDirectiveInput { return { - content: this.content + content: this.content, + id: this.id, + iconPlacement: 'left', + tooltipPlacement: ['bottom'] }; } } -// tslint:disable-next-line:max-classes-per-file -class MockTranslateService { - messages: {[index: string]: string} = { - lorem: 'lorem ipsum dolor sit amet', - linkTest: 'This is text, [this](https://dspace.lyrasis.org) is a link, and [so is this](https://google.com)' - }; - - get(key: string): Observable { - return observableOf(this.messages[key]); - } -} - +const messages = { + lorem: 'lorem ipsum dolor sit amet', + linkTest: 'This is text, [this](https://dspace.lyrasis.org) is a link, and [so is this](https://google.com)' +}; +const exampleContextHelp: ContextHelp = { + id: 'test-tooltip', + isTooltipVisible: false +}; describe('ContextHelpDirective', () => { let component: TestComponent; let fixture: ComponentFixture; // let el: DebugElement; - // let translateService: TranslateService; + let translateService: any; + let contextHelpService: any; + let getContextHelp$: BehaviorSubject; + let shouldShowIcons$: BehaviorSubject; - beforeEach(() => { - console.log('Anyone hear that?'); - fixture = TestBed.configureTestingModule({ + beforeEach(waitForAsync(() => { + translateService = jasmine.createSpyObj('translateService', ['get']); + contextHelpService = jasmine.createSpyObj('contextHelpService', [ + 'shouldShowIcons$', + 'getContextHelp$', + 'add', + 'remove', + 'toggleIcons', + 'toggleTooltip', + 'showTooltip', + 'hideTooltip' + ]); + + TestBed.configureTestingModule({ imports: [NgbTooltipModule], providers: [ - { provide: TranslateService, useClass: MockTranslateService } + { provide: TranslateService, useValue: translateService }, + { provide: ContextHelpService, useValue: contextHelpService } ], declarations: [TestComponent, ContextHelpWrapperComponent, ContextHelpDirective] - }).createComponent(TestComponent); + }).compileComponents() + })); + + beforeEach(() => { + // Set up service behavior. + getContextHelp$ = new BehaviorSubject(exampleContextHelp); + shouldShowIcons$ = new BehaviorSubject(false); + contextHelpService.getContextHelp$.and.returnValue(getContextHelp$); + contextHelpService.shouldShowIcons$.and.returnValue(shouldShowIcons$); + translateService.get.and.callFake((content) => observableOf(messages[content])); + + // Set up fixture and component. + fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; + component.id = 'test-tooltip'; + component.content = 'lorem'; + }); - it('should add the tooltip icon', () => { - component.content = 'lorem'; + it('should generate the context help wrapper component', (done) => { fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(fixture.nativeElement.children.length).toBe(1); + let [wrapper] = fixture.nativeElement.children; + expect(component).toBeDefined(); + expect(wrapper.tagName).toBe('DS-CONTEXT-HELP-WRAPPER'); + expect(contextHelpService.add).toHaveBeenCalledWith(exampleContextHelp) + done(); + }); + }); - expect(component).toBeDefined(); - const [wrapper] = fixture.nativeElement.children; - const [i, div] = wrapper.children; - expect(wrapper.tagName).toBe('DS-CONTEXT-HELP-WRAPPER'); - expect(i.tagName).toBe('I'); - expect(div.tagName).toBe('DIV'); - expect(div.innerHTML).toBe('some text'); - i.click(); // TODO: triggers a type error + it('should not show the context help button while icon visibility is not turned on', (done) => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + let wrapper = matchWrapper(fixture.nativeElement); + verifyNoButton(wrapper); + done(); + }); + }); + + describe('when icon visibility is toggled on', () => { + beforeEach(() => { + shouldShowIcons$.next(true); + }); + + it('should show the context help button', (done) => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + let wrapper = matchWrapper(fixture.nativeElement); + let i = verifyButton(wrapper); + i.click(); + expect(contextHelpService.toggleTooltip).toHaveBeenCalledWith('test-tooltip'); + getContextHelp$.next({id: 'test-tooltip', isTooltipVisible: true}); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(wrapper.parentElement.parentElement.querySelector('ngb-tooltip-window')).toBeTruthy(); + done(); + }); + }); + }); }); }); + +function matchWrapper(el: HTMLElement): HTMLElement { + expect(el.children.length).toBe(1); + return el.children[0] as HTMLElement; +} + +function verifyNoButton(wrapper: HTMLElement) { + expect(wrapper.children.length).toBe(1); + let [div] = wrapper.children; + expect(div.tagName).toBe('DIV'); +} + +function verifyButton(wrapper: Element): HTMLElement { + expect(wrapper.children.length).toBe(2); + let [i, div] = wrapper.children; + expect(i.tagName).toBe('I'); + expect(div.tagName).toBe('DIV'); + return i as HTMLElement; +} diff --git a/src/app/shared/context-help.directive.ts b/src/app/shared/context-help.directive.ts index 56e7514b98..83c3908dcf 100644 --- a/src/app/shared/context-help.directive.ts +++ b/src/app/shared/context-help.directive.ts @@ -54,9 +54,11 @@ export class ContextHelpDirective implements OnChanges, OnDestroy { this.mostRecentId = this.dsContextHelp.id; this.contextHelpService.add({id: this.dsContextHelp.id, isTooltipVisible: false}); - const factory - = this.componentFactoryResolver.resolveComponentFactory(ContextHelpWrapperComponent); - this.wrapper = this.viewContainerRef.createComponent(factory); + if (this.wrapper === undefined) { + const factory + = this.componentFactoryResolver.resolveComponentFactory(ContextHelpWrapperComponent); + this.wrapper = this.viewContainerRef.createComponent(factory); + } this.wrapper.instance.templateRef = this.templateRef; this.wrapper.instance.content = this.dsContextHelp.content; this.wrapper.instance.id = this.dsContextHelp.id;