343 lines
9.9 KiB
TypeScript
343 lines
9.9 KiB
TypeScript
import { log, Enums } from '@ohif/core';
|
|
import { EVENTS } from '@cornerstonejs/core';
|
|
import initViewTiming from './initViewTiming';
|
|
|
|
jest.mock('@ohif/core', () => ({
|
|
log: {
|
|
timingKeys: {},
|
|
timeEnd: jest.fn(),
|
|
},
|
|
Enums: {
|
|
TimingEnum: {
|
|
DISPLAY_SETS_TO_ALL_IMAGES: 'DISPLAY_SETS_TO_ALL_IMAGES',
|
|
DISPLAY_SETS_TO_FIRST_IMAGE: 'DISPLAY_SETS_TO_FIRST_IMAGE',
|
|
STUDY_TO_FIRST_IMAGE: 'STUDY_TO_FIRST_IMAGE',
|
|
SCRIPT_TO_VIEW: 'SCRIPT_TO_VIEW',
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock('@cornerstonejs/core', () => ({
|
|
EVENTS: {
|
|
IMAGE_RENDERED: 'IMAGE_RENDERED',
|
|
},
|
|
}));
|
|
|
|
describe('initViewTiming', () => {
|
|
const mockElement = {
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
};
|
|
|
|
const defaultParameters = {
|
|
element: mockElement,
|
|
};
|
|
|
|
const clearUnevenlyHandledViewportState = () => {
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
imageRenderedListener({
|
|
detail: {
|
|
viewportStatus: 'rendered',
|
|
element: mockElement,
|
|
},
|
|
});
|
|
};
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
// @ts-expect-error - restarting the object that will have values set by each test
|
|
log.timingKeys = {};
|
|
});
|
|
|
|
it('should return early when no timing keys are set', () => {
|
|
// @ts-expect-error - the idea is to test an invalid state
|
|
log.timingKeys = {};
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should add event listener when timing keys are present', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).toHaveBeenCalledWith(
|
|
EVENTS.IMAGE_RENDERED,
|
|
expect.any(Function)
|
|
);
|
|
|
|
clearUnevenlyHandledViewportState();
|
|
});
|
|
|
|
it('should handle multiple timing keys', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES] = true;
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).toHaveBeenCalledWith(
|
|
EVENTS.IMAGE_RENDERED,
|
|
expect.any(Function)
|
|
);
|
|
|
|
clearUnevenlyHandledViewportState();
|
|
});
|
|
|
|
it('should return early in imageRenderedListener when viewportStatus is preRender', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {
|
|
detail: {
|
|
viewportStatus: 'preRender',
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener(evt);
|
|
|
|
expect(log.timeEnd).not.toHaveBeenCalled();
|
|
expect(mockElement.removeEventListener).not.toHaveBeenCalled();
|
|
|
|
clearUnevenlyHandledViewportState();
|
|
});
|
|
|
|
it('should call timeEnd for timing keys when image is rendered', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {
|
|
detail: {
|
|
viewportStatus: 'rendered',
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener(evt);
|
|
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.STUDY_TO_FIRST_IMAGE);
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.SCRIPT_TO_VIEW);
|
|
});
|
|
|
|
it('should remove event listener after image is rendered', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {
|
|
detail: {
|
|
viewportStatus: 'rendered',
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener(evt);
|
|
|
|
expect(mockElement.removeEventListener).toHaveBeenCalledWith(
|
|
EVENTS.IMAGE_RENDERED,
|
|
imageRenderedListener
|
|
);
|
|
});
|
|
|
|
it('should not call timeEnd for ALL_IMAGES when viewports are still waiting', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {
|
|
detail: {
|
|
viewportStatus: 'rendered',
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener(evt);
|
|
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.STUDY_TO_FIRST_IMAGE);
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.SCRIPT_TO_VIEW);
|
|
expect(log.timeEnd).not.toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
|
|
|
|
clearUnevenlyHandledViewportState();
|
|
});
|
|
|
|
it('should handle multiple viewports finishing in sequence', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
const mockElement2 = {
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
};
|
|
|
|
initViewTiming(defaultParameters);
|
|
initViewTiming({ element: mockElement2 });
|
|
|
|
const imageRenderedListener1 = mockElement.addEventListener.mock.calls[0][1];
|
|
const imageRenderedListener2 = mockElement2.addEventListener.mock.calls[0][1];
|
|
|
|
const evt1 = {
|
|
detail: {
|
|
viewportStatus: 'rendered',
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
const evt2 = {
|
|
detail: {
|
|
viewportStatus: 'rendered',
|
|
element: mockElement2,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener1(evt1);
|
|
|
|
expect(log.timeEnd).not.toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
|
|
|
|
imageRenderedListener2(evt2);
|
|
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
|
|
});
|
|
|
|
it('should call timeEnd for ALL_IMAGES when last viewport finishes', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {
|
|
detail: {
|
|
viewportStatus: 'rendered',
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener(evt);
|
|
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
|
|
});
|
|
|
|
it('should handle different viewportStatus values', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {
|
|
detail: {
|
|
viewportStatus: 'loading',
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener(evt);
|
|
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
|
expect(mockElement.removeEventListener).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle undefined viewportStatus', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {
|
|
detail: {
|
|
element: mockElement,
|
|
},
|
|
};
|
|
|
|
imageRenderedListener(evt);
|
|
|
|
expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
|
expect(mockElement.removeEventListener).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle missing detail object', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1];
|
|
const evt = {};
|
|
|
|
expect(() => imageRenderedListener(evt)).toThrow();
|
|
});
|
|
|
|
it('should handle timing keys with falsy values', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = false;
|
|
log.timingKeys[Enums.TimingEnum.STUDY_TO_FIRST_IMAGE] = null;
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES] = undefined;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle timing keys with truthy values', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = 'some value';
|
|
log.timingKeys[Enums.TimingEnum.STUDY_TO_FIRST_IMAGE] = 1;
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES] = {};
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).toHaveBeenCalledWith(
|
|
EVENTS.IMAGE_RENDERED,
|
|
expect.any(Function)
|
|
);
|
|
});
|
|
|
|
it('should initialize IMAGE_TIMING_KEYS on first call', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle empty element object', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
const emptyElement = {};
|
|
|
|
expect(() => initViewTiming({ element: emptyElement })).toThrow();
|
|
});
|
|
|
|
it('should handle null element', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
expect(() => initViewTiming({ element: null })).toThrow();
|
|
});
|
|
|
|
it('should handle multiple calls with same element', () => {
|
|
log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true;
|
|
|
|
initViewTiming(defaultParameters);
|
|
initViewTiming(defaultParameters);
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).toHaveBeenCalledTimes(3);
|
|
});
|
|
|
|
it('should handle case where no timing keys match', () => {
|
|
// @ts-expect-error - the idea is to test an invalid state
|
|
log.timingKeys = {
|
|
someOtherKey: true,
|
|
anotherKey: 'value',
|
|
};
|
|
|
|
initViewTiming(defaultParameters);
|
|
|
|
expect(mockElement.addEventListener).not.toHaveBeenCalled();
|
|
});
|
|
});
|