test( zoomIn-tool): verify horizontal flip is preserved in magnified view (#5983)

This commit is contained in:
Ghadeer Albattarni authored and GitHub committed 2026-04-29 10:32:27 -04:00
1 parent 03860030bd
commit 62620afb9b
20 files changed
+384 -220

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
import { checkForScreenshot, screenShotPaths, test, visitStudy } from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should render the magnified region with the same horizontal flip as the source viewport', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.moreTools.flipHorizontal.click();
const activeViewport = await viewportPageObject.active;
await activeViewport.sliceNavigation.toSlice(61);
await mainToolbarPageObject.moreTools.magnify.click();
await activeViewport.magnifyGlass.drag({
start: { x: 0.78, y: 0.91 },
mouseUp: false,
});
await checkForScreenshot(
page,
activeViewport.magnifyGlass.locator,
screenShotPaths.zoomIn.magnifyViewportDisplayedCorrectly
);
await activeViewport.magnifyGlass.stopDrag();
});
+48
View File
@@ -0,0 +1,48 @@
import { Locator, Page } from '@playwright/test';
import { simulateNormalizedDragOnElement } from '../utils';
type MagnifyDragParams = {
start: { x: number; y: number };
end?: { x: number; y: number };
mouseUp?: boolean;
button?: 'left' | 'right' | 'middle';
delay?: number;
steps?: number;
};
export class MagnifyGlassPageObject {
readonly page: Page;
readonly viewport: Locator;
constructor(page: Page, viewport: Locator) {
this.page = page;
this.viewport = viewport;
}
get locator(): Locator {
return this.viewport.locator('.magnifyTool');
}
async drag({
start,
end,
button,
delay,
steps,
mouseUp = true,
}: MagnifyDragParams): Promise<void> {
await simulateNormalizedDragOnElement({
locator: this.viewport,
start,
end: end ?? start,
button,
delay,
steps,
mouseUp,
});
}
async stopDrag(): Promise<void> {
await this.page.mouse.up();
}
}
+10
View File
@@ -326,6 +326,16 @@ export class MainToolbarPageObject {
},
};
},
get magnify() {
const button = page.getByTestId('Magnify');
return {
button,
async click() {
await moreTools.click();
await button.click();
},
};
},
};
}
+63
View File
@@ -8,6 +8,7 @@ import {
} from '../utils';
import { DataOverlayPageObject } from './DataOverlayPageObject';
import { DOMOverlayPageObject } from './DOMOverlayPageObject';
import { MagnifyGlassPageObject } from './MagnifyGlassPageObject';
type SvgInnerElement = 'circle' | 'path' | 'd';
@@ -91,6 +92,13 @@ export interface IViewportPageObject {
click: () => Promise<void>;
};
};
sliceNavigation: {
toSlice: (sliceIndex: number) => Promise<void>;
toFirstSlice: () => Promise<void>;
toLastSlice: () => Promise<void>;
scrollBy: (delta: number) => Promise<void>;
};
magnifyGlass: MagnifyGlassPageObject;
}
export class ViewportPageObject {
@@ -200,6 +208,59 @@ export class ViewportPageObject {
};
}
/**
* Note: awaiting the returned methods (toSlice, toFirstSlice, toLastSlice, scrollBy)
* does not guarantee the viewport has finished rendering. Follow up with
* `waitForViewportsRendered` if you need pixel-stable state.
*/
private getSliceNavigation(viewport: Locator) {
const page = this.page;
const jumpToImage = async (imageIndex: number) => {
const viewportId = await this.getViewportId(viewport);
await page.evaluate(
({ commandsManager, viewportId, imageIndex }) => {
return commandsManager.runCommand('jumpToImage', {
imageIndex,
viewport: { id: viewportId },
});
},
{
viewportId,
imageIndex,
commandsManager: await page.evaluateHandle('window.commandsManager'),
}
);
};
const scrollBy = async (delta: number) => {
const viewportId = await this.getViewportId(viewport);
await page.evaluate(
({ services, viewportId, delta }) => {
const cornerstoneViewport = (
services as any
).cornerstoneViewportService.getCornerstoneViewport(viewportId);
if (!cornerstoneViewport) {
return;
}
return cornerstoneViewport.scroll(delta);
},
{
viewportId,
delta,
services: await page.evaluateHandle('window.services'),
}
);
};
return {
toSlice: jumpToImage,
toFirstSlice: () => jumpToImage(0),
toLastSlice: () => jumpToImage(-1),
scrollBy,
};
}
private async viewportPageObjectFactory(viewport: Locator): Promise<IViewportPageObject> {
return {
nthAnnotation: (nth: number) => this.getAnnotation(viewport, nth),
@@ -244,6 +305,8 @@ export class ViewportPageObject {
return this.getSvg(viewport, innerElement);
},
navigationArrows: this.getNavigationArrows(viewport),
sliceNavigation: this.getSliceNavigation(viewport),
magnifyGlass: new MagnifyGlassPageObject(this.page, viewport),
};
}
+2
View File
@@ -5,6 +5,7 @@ import { RightPanelPageObject } from './RightPanelPageObject';
import { ViewportPageObject } from './ViewportPageObject';
import { NotFoundStudyPageObject } from './NotFoundStudyPageObject';
import { DicomTagBrowserPageObject } from './DicomTagBrowserPageObject';
import { MagnifyGlassPageObject } from './MagnifyGlassPageObject';
export {
DOMOverlayPageObject,
@@ -14,4 +15,5 @@ export {
ViewportPageObject,
NotFoundStudyPageObject,
DicomTagBrowserPageObject,
MagnifyGlassPageObject,
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+3
View File
@@ -97,6 +97,9 @@ const screenShotPaths = {
reset: {
resetDisplayedCorrectly: 'resetDisplayedCorrectly.png',
},
zoomIn: {
magnifyViewportDisplayedCorrectly: 'magnifyViewportDisplayedCorrectly.png',
},
rtDataOverlayForUnreferencedDisplaySetNoHydration: {
overlayFirstImage: 'overlayFirstImage.png',
overlayMiddleImage: 'overlayMiddleImage.png',
+6 -2
View File
@@ -17,6 +17,7 @@ export async function simulateNormalizedDragOnElement({
button = 'left',
delay = 50,
steps = 10,
mouseUp = true,
}: {
locator: Locator;
start: { x: number; y: number };
@@ -24,6 +25,7 @@ export async function simulateNormalizedDragOnElement({
button?: 'left' | 'right' | 'middle';
delay?: number;
steps?: number;
mouseUp?: boolean;
}) {
const bBox = await locator.boundingBox();
if (!bBox) {
@@ -42,7 +44,7 @@ export async function simulateNormalizedDragOnElement({
// Get the page from the locator context
const page = locator.page();
page.mouse.move(startPosition.x, startPosition.y);
await page.mouse.move(startPosition.x, startPosition.y);
// Start drag
await page.mouse.down({ button });
@@ -63,5 +65,7 @@ export async function simulateNormalizedDragOnElement({
}
// End drag
await page.mouse.up({ button });
if (mouseUp) {
await page.mouse.up({ button });
}
}