chore: recovery 2 (#5014) merge to 3.11 (#5175)

Co-authored-by: Alireza <ar.sedghi@gmail.com>
Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
Co-authored-by: Tang Cheng <45505657+tctco@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2025-07-03 14:08:46 -04:00
1 parent 17ed63eb1d
commit 24e0263fb9
44 files changed
+516 -258

No files matched your search

+55 -17
View File
@@ -1,28 +1,34 @@
import { expect } from 'playwright-test-coverage';
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
) => {
type CheckForScreenshotProps = {
page: Page;
locator: Locator | Page;
screenshotPath: string;
attempts?: number;
delay?: number;
maxDiffPixelRatio?: number;
threshold?: number;
};
const _checkForScreenshot = async (props: CheckForScreenshotProps) => {
const {
page,
locator,
screenshotPath,
attempts = 10,
delay = 500,
maxDiffPixelRatio = 0.02,
threshold = 0.05,
} = props;
await page.waitForLoadState('networkidle');
for (let i = 0; i < attempts; i++) {
try {
await expect(locator).toHaveScreenshot(screenshotPath, {
// 4% tolerance for screenshot comparison
maxDiffPixelRatio: 0.04,
maxDiffPixelRatio,
threshold,
});
return true;
} catch (error) {
@@ -38,4 +44,36 @@ const checkForScreenshot = async (
throw new Error('Screenshot comparison failed: loop exited without match or proper error');
};
/**
* Checks if a screenshot of a specific element matches the expected screenshot.
* It retries the check for a specified number of attempts with a delay between each attempt.
* By default, the number of attempts is 10 and the delay is 500 milliseconds which results in a maximum wait time of 5 seconds.
* Instead of sleeping idle prior to calling this function, simply adjust the attempts and delay parameters to achieve the desired wait time.
* @param pageOrProps - The page to interact with or an object containing page and other properties
* @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 (
pageOrProps: Page | CheckForScreenshotProps,
locator?: Locator | Page,
screenshotPath?: string,
attempts?: number,
delay?: number
) => {
if (typeof pageOrProps === 'object' && 'page' in pageOrProps) {
return await _checkForScreenshot(pageOrProps as CheckForScreenshotProps);
} else {
return await _checkForScreenshot({
page: pageOrProps as Page,
locator,
screenshotPath,
attempts,
delay,
});
}
};
export { checkForScreenshot };
+9 -3
View File
@@ -1,9 +1,13 @@
import { visitStudy } from './visitStudy';
import { checkForScreenshot } from './checkForScreenshot';
import { screenShotPaths } from './screenShotPaths';
import { simulateClicksOnElement } from './simulateClicksOnElement';
import {
simulateClicksOnElement,
simulateNormalizedClickOnElement,
simulateNormalizedClicksOnElement,
} from './simulateClicksOnElement';
import { reduce3DViewportSize } from './reduce3DviewportSize';
import { getMousePosition, initilizeMousePositionTracker } from './mouseUtils';
import { getMousePosition, initializeMousePositionTracker } from './mouseUtils';
import { getSUV } from './getSUV';
import { getTMTVModalityUnit } from './getTMTVModalityUnit';
import { clearAllAnnotations } from './clearAllAnnotations';
@@ -16,9 +20,11 @@ export {
checkForScreenshot,
screenShotPaths,
simulateClicksOnElement,
simulateNormalizedClickOnElement,
simulateNormalizedClicksOnElement,
reduce3DViewportSize,
getMousePosition,
initilizeMousePositionTracker,
initializeMousePositionTracker,
getSUV,
getTMTVModalityUnit,
clearAllAnnotations,
+6 -6
View File
@@ -5,21 +5,21 @@ interface WindowWithMousePosition extends Window {
mouseY: number;
}
export const initilizeMousePositionTracker = async (page: Page) => {
const window = await page.evaluateHandle("window") as any;
export const initializeMousePositionTracker = 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.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;
const window = (await page.evaluateHandle('window')) as any;
return await page.evaluate((window: WindowWithMousePosition) => {
return { x: window.mouseX, y: window.mouseY };
}, window);
}
};
+4
View File
@@ -14,6 +14,10 @@ const screenShotPaths = {
cobbangle: {
cobbangleDisplayedCorrectly: 'cobbangleDisplayedCorrectly.png',
},
contextMenu: {
preContextMenuNearBottomEdge: 'preContextMenuNearBottomEdge.png',
contextMenuNearBottomEdgeNotClipped: 'contextMenuNearBottomEdgeNotClipped.png',
},
ellipse: {
ellipseDisplayedCorrectly: 'ellipseDisplayedCorrectly.png',
},
+46
View File
@@ -17,3 +17,49 @@ export async function simulateClicksOnElement({
await locator.click({ delay: 100, position: { x, y } });
}
}
/**
* Simulates clicks on an element at a normalized point.
*
* @param locator - The locator to click on.
* @param normalizedPoint - The point with x and y coordinates, normalized to the element's bounding box.
* @param button - The mouse button to use for the click (default is 'left').
*/
export async function simulateNormalizedClickOnElement({
locator,
normalizedPoint,
button = 'left',
}: {
locator: Locator;
normalizedPoint: { x: number; y: number };
button?: 'left' | 'right' | 'middle';
}) {
const bBox = await locator.boundingBox();
const position = { x: normalizedPoint.x * bBox.width, y: normalizedPoint.y * bBox.height };
await locator.click({ delay: 100, position, button });
}
/**
* Simulates clicks on an element at normalized points.
*
* @param locator - The locator to click on.
* @param normalizedPoints - An array of points with x and y coordinates, normalized to the element's bounding box.
* @param button - The mouse button to use for the click (default is 'left').
*/
export async function simulateNormalizedClicksOnElement({
locator,
normalizedPoints,
button = 'left',
}: {
locator: Locator;
normalizedPoints: { x: number; y: number }[];
button?: 'left' | 'right' | 'middle';
}) {
for (const normalizedPoint of normalizedPoints) {
await simulateNormalizedClickOnElement({
locator,
normalizedPoint,
button,
});
}
}