import { inflateInitialScanState, inflateScanState, } from '@/functions/modelHelper'; import { AnchorPoint, FrontEndInterfaces, PositionData, ScanMovementActions, SlidersActions, } from '@3cr/types-ts'; import { flushPromises, mount } from '@vue/test-utils'; import { spyOn } from '@vitest/spy'; import { cSlider, currentGreyscalePreset, isLayout1x3, isLayout2x2, setInitialScanStateFromPayload, setScanState, sSlider, thresholdSlider, transactionStarted, tSlider, windowSlider, } from '@/models/scanState'; import { defineComponent, ref, unref } from 'vue'; import { Viewer3crServiceImpl } from '@/services/viewer-3cr.service'; import { VDialog } from 'vuetify/components'; import Viewer3cr from '@/components/modal/Viewer3cr.vue'; import SettingsMenu from '@/components/modal/menus/SettingsMenu.vue'; import { useDemoStore } from '@/stores/demo.store'; import { storeToRefs } from 'pinia'; import { useViewer3cr } from '@/composables/useViewer3cr'; import { MockInstance } from '@vitest/spy'; import ViewerNavigationDrawer from '@/components/modal/ViewerNavigationDrawer.vue'; import ViewerAppBar from '@/components/modal/app-bars/ViewerAppBar.vue'; import DemoNotificationNavigationDrawer from '@/components/demo/DemoNotificationNavigationDrawer.vue'; import ViewerIntro from '@/components/intro/ViewerIntro.vue'; import ViewerTour from '@/components/intro/ViewerTour.vue'; import { ViewerPayload } from '@3cr/viewer-types-ts'; let spy: MockInstance; const app = defineComponent({ template: '', components: { Viewer3cr }, setup() { const webgl = ref(); return { webgl }; }, }); describe('Viewer3cr tests', () => { const viewer3cr = useViewer3cr() as Viewer3crServiceImpl; const { isDemo } = storeToRefs(useDemoStore()); let wrapper = mount(app, { global: { stubs: ['Viewer3crWebGL'] } }); spy = spyOn(viewer3cr, 'sendPayload').mockResolvedValue(); beforeEach(() => { transactionStarted.value = false; spy = spyOn(viewer3cr, 'sendPayload').mockResolvedValue(); wrapper = mount(app, { global: { stubs: ['Viewer3crWebGL'] } }); }); afterEach(() => { isDemo.value = false; vi.clearAllMocks(); wrapper.unmount(); }); it('inflates', () => { expect(wrapper).toBeTruthy(); }); describe('watchers', () => { it('scanState.value.Display.Brightness', async () => { await testWatcherForDisplay('Brightness', SlidersActions.sl01); }); it('scanState.value.Display.Contrast', async () => { await testWatcherForDisplay('Contrast', SlidersActions.sl02); }); it('scanState.value.Display.Opacity', async () => { await testWatcherForDisplay('Opacity', SlidersActions.sl03); }); it('scanState.value.Display.WindowLower', async () => { await testWatcherForDisplay('WindowLower', SlidersActions.sl04); }); it('scanState.value.Display.WindowUpper', async () => { await testWatcherForDisplay('WindowUpper', SlidersActions.sl05); }); it('scanState.value.Display.ThresholdLower', async () => { await testWatcherForDisplay('ThresholdLower', SlidersActions.sl06); }); it('scanState.value.Display.ThresholdUpper', async () => { await testWatcherForDisplay('ThresholdUpper', SlidersActions.sl07); }); it('scanState.value.Slice.TransverseLower', async () => { await testWatcherForSlice('TransverseLower', SlidersActions.sl08); }); it('scanState.value.Orientations.Transverse.Slice', async () => { await testWatcherForOrientation('Transverse', SlidersActions.sl09); }); it('scanState.value.Slice.TransverseUpper', async () => { await testWatcherForSlice('TransverseUpper', SlidersActions.sl10); }); it('scanState.value.Slice.SagittalLower', async () => { await testWatcherForSlice('SagittalLower', SlidersActions.sl11); }); it('scanState.value.Orientations.Sagittal.Slice', async () => { await testWatcherForOrientation('Sagittal', SlidersActions.sl12); }); it('scanState.value.Slice.SagittalUpper', async () => { await testWatcherForSlice('SagittalUpper', SlidersActions.sl13); }); it('scanState.value.Slice.CoronalLower', async () => { await testWatcherForSlice('CoronalLower', SlidersActions.sl14); }); it('scanState.value.Orientations.Coronal.Slice', async () => { await testWatcherForOrientation('Coronal', SlidersActions.sl15); }); it('scanState.value.Slice.CoronalUpper', async () => { await testWatcherForSlice('CoronalUpper', SlidersActions.sl16); }); it('scanState.value.Slice.CoronalUpper transaction disabled', async () => { transactionStarted.value = true; await testWatcherForSlice('CoronalUpper', SlidersActions.sl16, true); }); it('scanState.value.InteractionSettings.PanSensivitity transaction disabled', async () => { transactionStarted.value = true; await testWatcherForInteractionSettings( 'PanSensivitity1', ScanMovementActions.sm05, true, ); }); it('scanState.value.InteractionSettings.PanSensivitity', async () => { await testWatcherForInteractionSettings( 'PanSensivitity', ScanMovementActions.sm05, ); }); it('scanState.value.InteractionSettings.ZoomSensitivity', async () => { await testWatcherForInteractionSettings( 'ZoomSensitivity', ScanMovementActions.sm08, ); }); it('scanState.value.InteractionSettings.RotateSensitivity', async () => { await testWatcherForInteractionSettings( 'RotateSensitivity', ScanMovementActions.sm10, ); }); it('scanState.value.InteractionSettings.CameraRotateSensitivity', async () => { await testWatcherForInteractionSettings( 'CameraRotateSensitivity', ScanMovementActions.sm12, ); }); }); it('watches scanState', () => { expect(wrapper).toBeTruthy(); }); it('tSlider should be set', () => { expect(unref(tSlider)).toStrictEqual([0, 0]); }); it('sSlider should be set', () => { expect(unref(sSlider)).toStrictEqual([0, 0]); }); it('cSlider should be set', () => { expect(unref(cSlider)).toStrictEqual([0, 0]); }); it('windowSlider should set', async () => { const newVals: [number, number] = [20, 20]; windowSlider.value = newVals; await flushPromises(); expect(windowSlider.value).toStrictEqual(newVals); }); it('thresholdSlider should set', async () => { const newVals: [number, number] = [20, 20]; thresholdSlider.value = newVals; await flushPromises(); expect(thresholdSlider.value).toStrictEqual(newVals); }); it('tSlider should set', async () => { const newVals: [number, number] = [0, 0]; tSlider.value = newVals; await flushPromises(); expect(tSlider.value).toStrictEqual(newVals); }); it('sSlider should set', async () => { const newVals: [number, number] = [0, 0]; sSlider.value = newVals; await flushPromises(); expect(sSlider.value).toStrictEqual(newVals); }); it('cSlider should set', async () => { const newVals: [number, number] = [0, 0]; cSlider.value = newVals; await flushPromises(); expect(cSlider.value).toStrictEqual(newVals); }); it('should isLayout2x2', async () => { const scanState = inflateScanState(); scanState.Layout.PositionData = [ { Anchor: AnchorPoint.TOP_LEFT } as PositionData, { Anchor: AnchorPoint.TOP_RIGHT } as PositionData, { Anchor: AnchorPoint.BOTTOM_LEFT } as PositionData, { Anchor: AnchorPoint.BOTTOM_RIGHT } as PositionData, ]; setScanState(JSON.stringify(scanState)); expect(unref(isLayout2x2)).toBeTruthy(); expect(unref(isLayout1x3)).toBeFalsy(); }); it('should isLayout1x3', async () => { const scanState = inflateScanState(); scanState.Layout.PositionData = [ { Anchor: AnchorPoint.CENTER } as PositionData, { Anchor: AnchorPoint.TOP_RIGHT } as PositionData, { Anchor: AnchorPoint.RIGHT } as PositionData, { Anchor: AnchorPoint.BOTTOM_RIGHT } as PositionData, ]; setScanState(JSON.stringify(scanState)); expect(unref(isLayout1x3)).toBeTruthy(); expect(unref(isLayout2x2)).toBeFalsy(); }); it('should getCurrentGreyscalePreset', async () => { const initial = inflateInitialScanState(); initial.GreyscalePresets = [ { Name: 'testing', Lower: 30, Upper: 60, Version: '1.0.0' }, ]; setInitialScanStateFromPayload(JSON.stringify(initial)); await flushPromises(); const scanState = inflateScanState(); scanState.Display.WindowLower = 30; scanState.Display.WindowUpper = 60; setScanState(JSON.stringify(scanState)); await flushPromises(); expect(unref(currentGreyscalePreset)?.Name).toBe('testing'); }); it('should getCurrentGreyscalePreset', async () => { const initial = inflateInitialScanState(); initial.GreyscalePresets = [ { Name: 'testing', Lower: 30, Upper: 60, Version: '1.0.0' }, ]; initial.HuLower = 40; initial.HuUpper = 50; setInitialScanStateFromPayload(JSON.stringify(initial)); await flushPromises(); const scanState = inflateScanState(); scanState.Display.WindowLower = 40; scanState.Display.WindowUpper = 50; setScanState(JSON.stringify(scanState)); await flushPromises(); expect(unref(currentGreyscalePreset)?.Name).toBe('testing'); }); it('should getCurrentGreyscalePreset', async () => { const initial = inflateInitialScanState(); initial.GreyscalePresets = [ { Name: 'testing', Lower: 30, Upper: 60, Version: '1.0.0' }, ]; initial.HuLower = 40; initial.HuUpper = 50; setInitialScanStateFromPayload(JSON.stringify(initial)); await flushPromises(); const scanState = inflateScanState(); scanState.Display.WindowLower = 45; scanState.Display.WindowUpper = 46; setScanState(JSON.stringify(scanState)); await flushPromises(); expect(unref(currentGreyscalePreset)).toBe(undefined); }); it('should getCurrentGreyscalePreset', async () => { expect(unref(currentGreyscalePreset)).toBe(undefined); }); it('should close dialog', async () => { const wrapper = mount(Viewer3cr); expect(wrapper.vm.m_closeDialog).toBe(false); wrapper.findComponent(ViewerNavigationDrawer).vm.$emit('close'); expect(wrapper.vm.m_closeDialog).toBe(true); }); it('should close dialog appbar', async () => { const wrapper = mount(Viewer3cr); expect(wrapper.vm.m_closeDialog).toBe(false); wrapper.findComponent(ViewerAppBar).vm.$emit('close'); expect(wrapper.vm.m_closeDialog).toBe(true); }); it('should ViewerIntro emit start', async () => { const wrapper = mount(Viewer3cr); expect(wrapper.vm.tourActive).toBe(false); wrapper.findComponent(ViewerIntro).vm.$emit('start'); await flushPromises(); expect(wrapper.vm.tourActive).toBe(true); }); it('should ViewerIntro set value', async () => { const wrapper = mount(Viewer3cr); expect(wrapper.vm.introActive).toBe(false); await wrapper.findComponent(ViewerIntro).setValue(true); expect(wrapper.vm.introActive).toBe(true); }); it('should ViewerTour set value', async () => { const wrapper = mount(Viewer3cr); expect(wrapper.vm.tourActive).toBe(false); await wrapper.findComponent(ViewerTour).setValue(true); expect(wrapper.vm.tourActive).toBe(true); }); it('should setValue', async () => { const wrapper = mount(Viewer3cr); const { notificationDropdown, isDemo } = storeToRefs(useDemoStore()); isDemo.value = true; await wrapper.setProps({ payload: { Url: 'https://webgl-3dr.singular.health/demo-1', } as ViewerPayload, }); await flushPromises(); expect(notificationDropdown.value).toStrictEqual({ modal: false, x: 0, y: 0, }); await wrapper .findComponent(DemoNotificationNavigationDrawer) .setValue(true); expect(notificationDropdown.value).toStrictEqual({ modal: true, x: 0, y: 0, }); }); it('should v-model modals', async () => { const wrapper = mount(Viewer3cr); const modals = wrapper.findAllComponents(VDialog); for (const modal of modals) { await modal.setValue(false); } }); it('should set settings-menu model', async () => { await wrapper.findComponent(SettingsMenu).setValue(false); }); }); async function testWatcherForDisplay( key: string, action: SlidersActions, not: boolean = false, ) { const scanState = inflateScanState(); (scanState.Display as any)[key] = 60; setScanState(JSON.stringify(scanState)); await flushPromises(); if (not) { expect(spy).not.toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, { message: { Version: '0.0.1', Value: (scanState.Display as any)[key], }, }); } else { expect(spy).toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, { message: { Version: '0.0.1', Value: (scanState.Display as any)[key], }, }); } } async function testWatcherForSlice( key: string, action: SlidersActions, not: boolean = false, ) { const scanState = inflateScanState(); (scanState.Slice as any)[key] = 100; setScanState(JSON.stringify(scanState)); await flushPromises(); if (not) { expect(spy).not.toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, { message: { Version: '0.0.1', Value: (scanState.Slice as any)[key], }, }); } else { expect(spy).toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, { message: { Version: '0.0.1', Value: (scanState.Slice as any)[key], }, }); } } async function testWatcherForOrientation( key: string, action: SlidersActions, not: boolean = false, ) { const scanState = inflateScanState(); (scanState.Orientations as any)[key].Slice = 100; setScanState(JSON.stringify(scanState)); await flushPromises(); if (not) { expect(spy).not.toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, { message: { Version: '0.0.1', Value: (scanState.Orientations as any)[key].Slice, }, }); } else { expect(spy).toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, { message: { Version: '0.0.1', Value: (scanState.Orientations as any)[key].Slice, }, }); } } async function testWatcherForInteractionSettings( key: string, action: ScanMovementActions, not: boolean = false, ) { const scanState = inflateScanState(); (scanState.InteractionSettings as any)[key] = 100; setScanState(JSON.stringify(scanState)); await flushPromises(); if (not) { expect(spy).not.toHaveBeenCalledWith( FrontEndInterfaces.scan_movement, action, { message: { Version: '0.0.1', Value: (scanState.InteractionSettings as any)[key], }, }, ); } else { expect(spy).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, action, { message: { Version: '0.0.1', Value: (scanState.InteractionSettings as any)[key], }, }); } }