This commit is contained in:
mario
2025-03-07 13:47:44 +07:00
commit c4efec5a14
3358 changed files with 303774 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
import { expect } from '@playwright/test';
import { Locator, Page } from 'playwright';
/**
* @param page - The page to interact with
* @param locator - The element to check for screenshot
* @param screenshotPath - The path to save the screenshot
* @param attempts - The number of attempts to check for screenshot
* @param delay - The delay between attempts
* @returns True if the screenshot matches, otherwise throws an error
*/
const checkForScreenshot = async (
page: Page,
locator: Locator | Page,
screenshotPath: string,
attempts = 10,
delay = 100
) => {
await page.waitForLoadState('networkidle');
for (let i = 1; i < attempts; i++) {
try {
await expect(locator).toHaveScreenshot(screenshotPath, {
maxDiffPixelRatio: 0.1,
});
return true;
} catch (error) {
if (i === attempts) {
throw new Error('Screenshot does not match.');
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
};
export { checkForScreenshot };

View File

@@ -0,0 +1,10 @@
const clearAllAnnotations = async page => {
await page.evaluate(
({ cornerstoneTools }: AppTypes.Test) => {
cornerstoneTools.annotation.state.removeAllAnnotations();
},
await page.evaluateHandle('window')
);
};
export { clearAllAnnotations };

15
tests/utils/getSUV.ts Normal file
View File

@@ -0,0 +1,15 @@
const getSUV = async page => {
const SUV = await page.evaluate(
({ services }: AppTypes.Test) => {
const { measurementService } = services;
const measurements = measurementService.getMeasurements();
const displayText = measurements[0].displayText;
return displayText[2];
},
await page.evaluateHandle('window')
);
return SUV;
};
export { getSUV };

View File

@@ -0,0 +1,27 @@
const getTMTVModalityUnit = async (page, attempts = 20) => {
for (let i = 0; i < attempts; i++) {
try {
const modalityUnit = await page.evaluate(
({ cornerstoneTools }: AppTypes.Test) => {
const annotations = cornerstoneTools.annotation.state.getAllAnnotations();
const stats = annotations[0].data.cachedStats;
const targetIds = Object.keys(stats);
const targetStats = stats[targetIds[1]];
return targetStats.modalityUnit;
},
await page.evaluateHandle('window')
);
if (modalityUnit) {
return modalityUnit;
}
} catch (error) {
console.error('Failed to get modalityUnit', error);
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error('Failed to get modalityUnit after multiple attempts');
};
export { getTMTVModalityUnit };

24
tests/utils/index.ts Normal file
View File

@@ -0,0 +1,24 @@
import { visitStudy } from './visitStudy';
import { checkForScreenshot } from './checkForScreenshot';
import { screenShotPaths } from './screenShotPaths';
import { simulateClicksOnElement } from './simulateClicksOnElement';
import { reduce3DViewportSize } from './reduce3DviewportSize';
import { getMousePosition, initilizeMousePositionTracker } from './mouseUtils';
import { getSUV } from './getSUV';
import { getTMTVModalityUnit } from './getTMTVModalityUnit';
import { clearAllAnnotations } from './clearAllAnnotations';
import { scrollVolumeViewport } from './scrollVolumeViewport';
export {
visitStudy,
checkForScreenshot,
screenShotPaths,
simulateClicksOnElement,
reduce3DViewportSize,
getMousePosition,
initilizeMousePositionTracker,
getSUV,
getTMTVModalityUnit,
clearAllAnnotations,
scrollVolumeViewport,
};

25
tests/utils/mouseUtils.ts Normal file
View File

@@ -0,0 +1,25 @@
import { Page } from "@playwright/test";
interface WindowWithMousePosition extends Window {
mouseX: number;
mouseY: number;
}
export const initilizeMousePositionTracker = async (page: Page) => {
const window = await page.evaluateHandle("window") as any;
await page.evaluate((window: WindowWithMousePosition) => {
window.mouseX = 0;
window.mouseY = 0;
window.addEventListener("mousemove", (event) => {
window.mouseX = event.clientX;
window.mouseY = event.clientY;
});
}, window);
}
export const getMousePosition = async (page: Page) => {
const window = await page.evaluateHandle("window") as any;
return await page.evaluate((window: WindowWithMousePosition) => {
return { x: window.mouseX, y: window.mouseY };
}, window);
}

View File

@@ -0,0 +1,13 @@
export const reduce3DViewportSize = async (page: any) => {
await page.evaluate(
({ cornerstone }: AppTypes.Test) => {
const enabledElement = cornerstone
.getEnabledElements()
.filter(element => element.viewport.type === 'volume3d')[0];
const { viewport } = enabledElement;
viewport.setZoom(0.5);
viewport.render();
},
await page.evaluateHandle('window')
);
};

View File

@@ -0,0 +1,95 @@
/**
* Paths to the screenshots of the tests.
*/
const screenShotPaths = {
angle: {
angleDisplayedCorrectly: 'angleDisplayedCorrectly.png',
},
bidirectional: {
bidirectionalDisplayedCorrectly: 'bidirectionalDisplayedCorrectly.png',
},
circle: {
circleDisplayedCorrectly: 'circleDisplayedCorrectly.png',
},
cobbangle: {
cobbangleDisplayedCorrectly: 'cobbangleDisplayedCorrectly.png',
},
ellipse: {
ellipseDisplayedCorrectly: 'ellipseDisplayedCorrectly.png',
},
length: {
lengthDisplayedCorrectly: 'lengthDisplayedCorrectly.png',
},
livewire: {
livewireDisplayedCorrectly: 'livewireDisplayedCorrectly.png',
},
mpr: {
mprDisplayedCorrectly: 'mprDisplayedCorrectly.png',
},
threeDFourUp: {
threeDFourUpDisplayedCorrectly: 'threeDFourUpDisplayedCorrectly.png',
},
threeDMain: {
threeDMainDisplayedCorrectly: 'threeDMainDisplayedCorrectly.png',
},
threeDPrimary: {
threeDPrimaryDisplayedCorrectly: 'threeDPrimaryDisplayedCorrectly.png',
},
threeDOnly: {
threeDOnlyDisplayedCorrectly: 'threeDOnlyDisplayedCorrectly.png',
},
axialPrimary: {
axialPrimaryDisplayedCorrectly: 'axialPrimaryDisplayedCorrectly.png',
},
probe: {
probeDisplayedCorrectly: 'probeDisplayedCorrectly.png',
},
rectangle: {
rectangleDisplayedCorrectly: 'rectangleDisplayedCorrectly.png',
},
spline: {
splineDisplayedCorrectly: 'splineDisplayedCorrectly.png',
},
dicomTagBrowser: {
dicomTagBrowserDisplayedCorrectly: 'dicomTagBrowserDisplayedCorrectly.png',
},
rotateRight: {
rotateRightDisplayedCorrectly: 'rotateRightDisplayedCorrectly.png',
},
invert: {
invertDisplayedCorrectly: 'invertDisplayedCorrectly.png',
},
flipHorizontal: {
flipHorizontalDisplayedCorrectly: 'flipHorizontalDisplayedCorrectly.png',
},
reset: {
resetDisplayedCorrectly: 'resetDisplayedCorrectly.png',
},
srHydration: {
srPostHydration: 'srPostHydration.png',
srPreHydration: 'srPreHydration.png',
srJumpToMeasurement: 'srJumpToMeasurement.png',
},
segHydration: {
segPostHydration: 'segPostHydration.png',
segPreHydration: 'segPreHydration.png',
segJumpToSegment: 'segJumpToSegment.png',
},
rtHydration: {
rtPostHydration: 'rtPostHydration.png',
rtPreHydration: 'rtPreHydration.png',
rtJumpToStructure: 'rtJumpToStructure.png',
},
crosshairs: {
crosshairsRendered: 'crosshairsRendered.png',
crosshairsRotated: 'crosshairsRotated.png',
crosshairsSlabThickness: 'crosshairsSlabThickness.png',
crosshairsResetToolbar: 'crosshairsResetToolbar.png',
crosshairsNewDisplayset: 'crosshairsNewDisplayset.png',
},
tmtvRendering: {
tmtvDisplayedCorrectly: 'tmtvDisplayedCorrectly.png',
},
};
export { screenShotPaths };

View File

@@ -0,0 +1,17 @@
type scrollVolumeViewportType = {
viewportId: string;
delta: number;
};
const scrollVolumeViewport = async (page, viewportId, delta) => {
await page.evaluate(
({ services, viewportId, delta }: withTestTypes<scrollVolumeViewportType>) => {
const { cornerstoneViewportService } = services;
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId) as any;
viewport.scroll(delta);
},
{ viewportId, delta, services: await page.evaluateHandle('window.services') }
);
};
export { scrollVolumeViewport };

View File

@@ -0,0 +1,19 @@
import { Locator } from 'playwright';
/**
*
* @parm locator - The locator to click on
* @param points - The points to click on
* @returns Promise<void>
*/
export async function simulateClicksOnElement({
locator,
points,
}: {
locator: Locator;
points: { x: number; y: number }[];
}) {
for (const { x, y } of points) {
await locator.click({ delay: 100, position: { x, y } });
}
}

24
tests/utils/visitStudy.ts Normal file
View File

@@ -0,0 +1,24 @@
import { Page } from '@playwright/test';
/**
* Visit the study
* @param page - The page to interact with
* @param title - The study instance UID of the study to visit
* @param mode - The mode to visit the study in
* @param delay - The delay to wait after visiting the study
* @param datasources - the data source to load the study from
*/
export async function visitStudy(
page: Page,
studyInstanceUID: string,
mode: string,
delay: number = 0,
datasources = 'ohif'
) {
await page.goto(`/?resultsPerPage=100&datasources=${datasources}`);
await page.getByTestId(studyInstanceUID).click();
await page.getByRole('button', { name: mode }).click();
await page.waitForLoadState('domcontentloaded');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(delay);
}