diff --git a/src/glue.ts b/src/glue.ts index c982af2d5ddf5ae7d712a95cb7e9fce1bc4717b9..d2c6756cd0d534834fddb900464e406317641cb7 100644 --- a/src/glue.ts +++ b/src/glue.ts @@ -747,12 +747,22 @@ export class ClickInterceptorService{ } run(ev: any){ + let intercepted = false for (const clickInc of this.clickInterceptorStack) { let runNext = false clickInc(ev, () => { runNext = true }) - if (!runNext) break + if (!runNext) { + intercepted = true + break + } } + + if (!intercepted) this.fallback(ev) + } + + fallback(_ev: any) { + // called when the call has not been intercepted } } diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..808e4e2e4432f97664d86ad0ae5fd40b27dadae3 --- /dev/null +++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts @@ -0,0 +1,144 @@ +import { CommonModule } from "@angular/common" +import { TestBed } from "@angular/core/testing" +import { MockStore, provideMockStore } from "@ngrx/store/testing" +import { ClickInterceptorService } from "src/glue" +import { PANELS } from "src/services/state/ngViewerState/constants" +import { ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors" +import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors" +import { viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions" +import { viewerStateCustomLandmarkSelector, viewerStateSelectedRegionsSelector } from "src/services/state/viewerState/selectors" +import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util" +import { NehubaGlueCmp } from "./nehubaViewerGlue.component" + +describe('> nehubaViewerGlue.component.ts', () => { + let mockStore: MockStore + beforeEach( () => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + ], + declarations: [ + NehubaGlueCmp + ], + providers: [ + provideMockStore(), + { + provide: CLICK_INTERCEPTOR_INJECTOR, + useFactory: (clickIntService: ClickInterceptorService) => { + return { + deregister: clickIntService.removeInterceptor.bind(clickIntService), + register: clickIntService.addInterceptor.bind(clickIntService) + } as ClickInterceptor + }, + deps: [ + ClickInterceptorService + ] + }, + ] + }).overrideComponent(NehubaGlueCmp, { + set: { + template: '', + templateUrl: null + } + }).compileComponents() + mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.FOUR_PANEL) + mockStore.overrideSelector(ngViewerSelectorPanelOrder, '0123') + mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true) + mockStore.overrideSelector(viewerStateCustomLandmarkSelector, []) + mockStore.overrideSelector(viewerStateSelectedRegionsSelector, []) + mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, []) + }) + + it('> can be init', () => { + const fixture = TestBed.createComponent(NehubaGlueCmp) + expect(fixture.componentInstance).toBeTruthy() + }) + + describe('> selectHoveredRegion', () => { + let dispatchSpy: jasmine.Spy + let clickIntServ: ClickInterceptorService + beforeEach(() => { + dispatchSpy = spyOn(mockStore, 'dispatch') + clickIntServ = TestBed.inject(ClickInterceptorService) + }) + afterEach(() => { + dispatchSpy.calls.reset() + }) + + describe('> if on hover is empty array', () => { + let fallbackSpy: jasmine.Spy + beforeEach(() => { + fallbackSpy = spyOn(clickIntServ, 'fallback') + TestBed.createComponent(NehubaGlueCmp) + clickIntServ.run(null) + }) + it('> dispatch not called', () => { + expect(dispatchSpy).not.toHaveBeenCalled() + }) + it('> fallback called', () => { + expect(fallbackSpy).toHaveBeenCalled() + }) + }) + + describe('> if on hover is non object array', () => { + let fallbackSpy: jasmine.Spy + + const testObj0 = { + segment: 'hello world' + } + beforeEach(() => { + fallbackSpy = spyOn(clickIntServ, 'fallback') + mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, ['hello world', testObj0]) + TestBed.createComponent(NehubaGlueCmp) + clickIntServ.run(null) + }) + it('> dispatch not called', () => { + expect(dispatchSpy).not.toHaveBeenCalled() + }) + it('> fallback called', () => { + expect(fallbackSpy).toHaveBeenCalled() + }) + }) + + describe('> if on hover array containing at least 1 obj, only dispatch the first obj', () => { + let fallbackSpy: jasmine.Spy + const testObj0 = { + segment: 'hello world' + } + const testObj1 = { + segment: { + foo: 'baz' + } + } + const testObj2 = { + segment: { + hello: 'world' + } + } + beforeEach(() => { + fallbackSpy = spyOn(clickIntServ, 'fallback') + mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, [testObj0, testObj1, testObj2]) + + }) + afterEach(() => { + fallbackSpy.calls.reset() + }) + it('> dispatch called with obj1', () => { + TestBed.createComponent(NehubaGlueCmp) + clickIntServ.run(null) + const { segment } = testObj1 + expect(dispatchSpy).toHaveBeenCalledWith( + viewerStateSetSelectedRegions({ + selectRegions: [segment] + } as any) + ) + }) + it('> fallback called (does not intercept)', () => { + TestBed.createComponent(NehubaGlueCmp) + clickIntServ.run(null) + expect(fallbackSpy).toHaveBeenCalled() + }) + }) + }) +}) diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts index 042b97bc443d9edede37fc2473a3bdc6f372e044..25861a93495e4fe93775e5610b2088cdd281d449 100644 --- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts +++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts @@ -282,10 +282,10 @@ export class NehubaGlueCmp implements IViewer, OnChanges, OnDestroy{ } const nehubaViewerSub = this.newViewer$.pipe( - tap(() => nehubaViewer$.next(null)), + tap(() => nehubaViewer$ && nehubaViewer$.next(null)), switchMap(this.waitForNehuba.bind(this)), map(() => this.nehubaContainerDirective.nehubaViewerInstance) - ).subscribe(viewer => nehubaViewer$.next(viewer)) + ).subscribe(viewer => nehubaViewer$ && nehubaViewer$.next(viewer)) this.onDestroyCb.push(() => nehubaViewerSub.unsubscribe()) /** @@ -550,11 +550,11 @@ export class NehubaGlueCmp implements IViewer, OnChanges, OnDestroy{ const setupViewerApiSub = this.newViewer$.pipe( tap(() => { - setViewerHandle(null) + setViewerHandle && setViewerHandle(null) }), switchMap(this.waitForNehuba.bind(this)) ).subscribe(() => { - setViewerHandle({ + setViewerHandle && setViewerHandle({ setNavigationLoc : (coord, realSpace?) => this.nehubaContainerDirective.nehubaViewerInstance.setNavigationState({ position : coord, positionReal : typeof realSpace !== 'undefined' ? realSpace : true, @@ -703,10 +703,14 @@ export class NehubaGlueCmp implements IViewer, OnChanges, OnDestroy{ } private selectHoveredRegion(ev: any, next: Function){ - if (!this.onhoverSegments || !(this.onhoverSegments.length)) return next() + /** + * If label indicies are not defined by the ontology, it will be a string in the format of `{ngId}#{labelIndex}` + */ + const trueOnhoverSegments = this.onhoverSegments && this.onhoverSegments.filter(v => typeof v === 'object') + if (!trueOnhoverSegments || (trueOnhoverSegments.length === 0)) return next() this.store$.dispatch( viewerStateSetSelectedRegions({ - selectRegions: this.onhoverSegments.slice(0, 1) + selectRegions: trueOnhoverSegments.slice(0, 1) }) ) next()