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 { createPinia, setActivePinia, 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';
import { createTestingPinia } from '@pinia/testing';
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 () => {
setActivePinia(createPinia());
const wrapper = mount(Viewer3cr, {
plugins: [createTestingPinia({ createSpy: vi.fn })]
});
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]
}
});
}
}