test(e2e): update Playwright screenshots and add better tests and assertions for area calculations (#6022)

* Add (better) assertions for area calculation fixes.
* Add SVG and measurement side panel area for freehand ROI test.
* Add Playwright viewport screenshot scope migration guide.
* Update cornerstonjs dependencies to 4.22.8. Ensure all versions of @babel/preset-env are 7.29.5.

---------

Co-authored-by: Ghadeer Albattarni <165973963+GhadeerAlbattarni@users.noreply.github.com>
This commit is contained in:
Joe BoccanfusoandGhadeer Albattarni authored and GitHub committed 2026-05-18 10:36:05 -04:00
1 parent cb0cbe702c
commit fe16e80cf3
174 files changed
+1071 -751

No files matched your search

+6 -2
View File
@@ -15,7 +15,11 @@ test.beforeEach(async ({ page }) => {
});
test.describe('3D four up Test', async () => {
test('should render 3D four up correctly.', async ({ page, mainToolbarPageObject }) => {
test('should render 3D four up correctly.', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.layoutSelection.threeDFourUp.click();
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
@@ -24,7 +28,7 @@ test.describe('3D four up Test', async () => {
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.threeDFourUp.threeDFourUpDisplayedCorrectly
);
});
+10 -2
View File
@@ -15,10 +15,18 @@ test.beforeEach(async ({ page }) => {
});
test.describe('3D main Test', async () => {
test('should render 3D main correctly.', async ({ page, mainToolbarPageObject }) => {
test('should render 3D main correctly.', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.layoutSelection.threeDMain.click();
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
await waitForViewportsRendered(page);
await checkForScreenshot(page, page, screenShotPaths.threeDMain.threeDMainDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.threeDMain.threeDMainDisplayedCorrectly
);
});
});
+6 -2
View File
@@ -15,14 +15,18 @@ test.beforeEach(async ({ page }) => {
});
test.describe('3D only Test', async () => {
test('should render 3D only correctly.', async ({ page, mainToolbarPageObject }) => {
test('should render 3D only correctly.', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.layoutSelection.threeDOnly.click();
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
await waitForViewportsRendered(page);
// Use a 4 percent diff pixel ratio to account for slight color differences in the 3D viewport
await checkForScreenshot({
page,
locator: page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.threeDOnly.threeDOnlyDisplayedCorrectly,
maxDiffPixelRatio: 0.04,
});
+6 -2
View File
@@ -15,14 +15,18 @@ test.beforeEach(async ({ page }) => {
});
test.describe('3D primary Test', async () => {
test('should render 3D primary correctly.', async ({ page, mainToolbarPageObject }) => {
test('should render 3D primary correctly.', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.layoutSelection.threeDPrimary.click();
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
await waitForViewportsRendered(page);
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.threeDPrimary.threeDPrimaryDisplayedCorrectly
);
});
+6 -1
View File
@@ -20,5 +20,10 @@ test('should display the angle tool', async ({
{ x: 550, y: 300 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.angle.angleDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.angle.angleDisplayedCorrectly
);
});
+7 -2
View File
@@ -7,11 +7,16 @@ test.beforeEach(async ({ page }) => {
});
test.describe('Axial Primary Test', async () => {
test('should render Axial Primary correctly.', async ({ page, mainToolbarPageObject }) => {
test('should render Axial Primary correctly.', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.layoutSelection.axialPrimary.click();
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.axialPrimary.axialPrimaryDisplayedCorrectly
);
});
+2 -1
View File
@@ -19,9 +19,10 @@ test('should display the bidirectional tool', async ({
{ x: 515, y: 339 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.bidirectional.bidirectionalDisplayedCorrectly
);
});
+5 -1
View File
@@ -19,5 +19,9 @@ test('should display the circle tool', async ({
{ x: 488, y: 247 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.circle.circleDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.circle.circleDisplayedCorrectly
);
});
+6 -1
View File
@@ -21,5 +21,10 @@ test('should display the cobb angle tool', async ({
{ x: 625, y: 291 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.cobbangle.cobbangleDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.cobbangle.cobbangleDisplayedCorrectly
);
});
+6 -2
View File
@@ -21,13 +21,17 @@ test('should the context menu completely on screen and is not clipped for a poin
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.contextMenu.preContextMenuNearBottomEdge);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.contextMenu.preContextMenuNearBottomEdge
);
await activeViewport.normalizedClickAt([{ x: 0.55, y: 0.98 }], 'right');
await checkForScreenshot({
page,
locator: page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.contextMenu.contextMenuNearBottomEdgeNotClipped,
});
});
+6 -1
View File
@@ -19,5 +19,10 @@ test('should display the ellipse tool', async ({
{ x: 508, y: 281 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.ellipse.ellipseDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.ellipse.ellipseDisplayedCorrectly
);
});
+7 -2
View File
@@ -6,11 +6,16 @@ test.beforeEach(async ({ page }) => {
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should flip the image horizontally', async ({ page, mainToolbarPageObject }) => {
test('should flip the image horizontally', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.moreTools.flipHorizontal.click();
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.flipHorizontal.flipHorizontalDisplayedCorrectly
);
});
+74 -1
View File
@@ -1,4 +1,10 @@
import { expect, test, visitStudy, subscribeToMeasurementAdded } from './utils';
import {
expect,
getAnnotationStats,
subscribeToMeasurementAdded,
test,
visitStudy,
} from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5';
@@ -32,3 +38,70 @@ test('should not fire MEASUREMENT_ADDED when clicking the annotation text', asyn
await measurementAdded.unsubscribe();
}
});
test('rectangle and freehand at identical coordinates should yield comparable area', async ({
page,
DOMOverlayPageObject,
mainToolbarPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
// Open measurements panel first — needed later for the delete action, and
// opening up-front means both shapes are drawn against the same viewport
// layout (so normalized coords don't shift between draws).
await rightPanelPageObject.toggle();
await rightPanelPageObject.measurementsPanel.select();
const activeViewport = await viewportPageObject.active;
// End on the left edge just shy of the start so cornerstone3D's proximity-
// close triggers via the standard mouseup path (which fires
// ANNOTATION_COMPLETED). Ending at the start point would enter interactive close-
// preview mode and suppress the completion event; ending further away
// would draw a diagonal that distorts the rectangular shape.
const corners = [
{ x: 0.3, y: 0.3 },
{ x: 0.55, y: 0.3 },
{ x: 0.55, y: 0.55 },
{ x: 0.3, y: 0.55 },
{ x: 0.3, y: 0.31 },
];
const [topLeft, , bottomRight] = corners;
await mainToolbarPageObject.measurementTools.rectangleROI.click();
await activeViewport.normalizedClickAt([topLeft, bottomRight]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
const rectangles = await getAnnotationStats(page, { toolName: 'RectangleROI' });
const rectArea = rectangles[0].firstTargetStats!.area as number;
const rectRow = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0);
const rectSvgLines = activeViewport.getSvgAnnotationStatTextLines(rectangles[0].annotationUID);
await expect(rectSvgLines.nth(0)).toHaveText(`Area: ${Math.round(rectArea)} mm²`);
await expect(rectRow.stats.primary.lines.nth(0)).toHaveText(`${Math.round(rectArea)} mm²`);
// Remove the rectangle so the freehand can drag through the same coords
// without grabbing the rectangle's handles.
await rectRow.actions.delete();
await mainToolbarPageObject.measurementTools.freehandROI.click();
await activeViewport.normalizedPathDragAt({
path: corners,
config: { steps: 20, delay: 30 },
});
const freehands = await getAnnotationStats(page, { toolName: 'PlanarFreehandROI' });
const freehandArea = freehands[0].firstTargetStats!.area as number;
const freehandRow = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0);
const freehandSvgLines = activeViewport.getSvgAnnotationStatTextLines(freehands[0].annotationUID);
await expect(freehandSvgLines.nth(0)).toHaveText(`Area: ${Math.round(freehandArea)} mm²`);
await expect(freehandRow.stats.primary.lines.nth(0)).toHaveText(`${Math.round(freehandArea)} mm²`);
const pctDiff = (Math.abs(rectArea - freehandArea) / rectArea) * 100;
console.log(
`Rectangle: ${rectArea.toFixed(2)} mm² | ` +
`Freehand: ${freehandArea.toFixed(2)} mm² | ` +
`diff: ${pctDiff.toFixed(2)}%`
);
expect(pctDiff).toBeLessThan(1);
});
+6 -2
View File
@@ -12,8 +12,12 @@ test.beforeEach(async ({ page }) => {
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should invert the image', async ({ page, mainToolbarPageObject }) => {
test('should invert the image', async ({ page, mainToolbarPageObject, viewportPageObject }) => {
await mainToolbarPageObject.moreTools.invert.click();
await waitForViewportsRendered(page);
await checkForScreenshot(page, page, screenShotPaths.invert.invertDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.invert.invertDisplayedCorrectly
);
});
+26 -6
View File
@@ -54,7 +54,11 @@ test('should hydrate in MPR correctly', async ({
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
// scroll away
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.initialDraw);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.jumpToMeasurementMPR.initialDraw
);
// Focus on the canvas first, then use mouse wheel to scroll away
await page.evaluate(() => {
@@ -78,11 +82,19 @@ test('should hydrate in MPR correctly', async ({
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.scrollAway);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.jumpToMeasurementMPR.scrollAway
);
await rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0).click();
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.jumpToMeasurementStack);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.jumpToMeasurementMPR.jumpToMeasurementStack
);
await mainToolbarPageObject.layoutSelection.MPR.click();
@@ -93,19 +105,27 @@ test('should hydrate in MPR correctly', async ({
await page.waitForTimeout(3000);
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.jumpInMPR);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.jumpToMeasurementMPR.jumpInMPR
);
await leftPanelPageObject.loadSeriesByDescription('Lung 3.0 CE');
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.changeSeriesInMPR);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.jumpToMeasurementMPR.changeSeriesInMPR
);
await rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0).click();
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.jumpToMeasurementMPR.jumpToMeasurementAfterSeriesChange
);
});
+16 -4
View File
@@ -38,7 +38,11 @@ test('should prevent editing of label map segmentations when panelSegmentation.d
// navigate to the 12th image and ensure the correct overlay is displayed
await press({ page, key: 'ArrowDown', nTimes: 11 });
await checkForScreenshot(page, page, screenShotPaths.labelMapSegLocking.globalLockedSegPreEdit);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.labelMapSegLocking.globalLockedSegPreEdit
);
// Attempt to erase the segmentations.
await rightPanelPageObject.labelMapSegmentationPanel.tools.eraser.click();
@@ -61,7 +65,11 @@ test('should prevent editing of label map segmentations when panelSegmentation.d
end: { x: 1.0, y: 0.75 },
});
await checkForScreenshot(page, page, screenShotPaths.labelMapSegLocking.globalLockedSegPostEdit);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.labelMapSegLocking.globalLockedSegPostEdit
);
});
test('should allow editing of label map segmentations when panelSegmentation.disableEditing is false', async ({
@@ -89,7 +97,11 @@ test('should allow editing of label map segmentations when panelSegmentation.dis
// navigate to the 12th image and ensure the correct overlay is displayed
await press({ page, key: 'ArrowDown', nTimes: 11 });
await checkForScreenshot(page, page, screenShotPaths.labelMapSegLocking.globalUnlockedSegPreEdit);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.labelMapSegLocking.globalUnlockedSegPreEdit
);
// Attempt to erase the segmentations.
await rightPanelPageObject.labelMapSegmentationPanel.tools.eraser.click();
@@ -114,7 +126,7 @@ test('should allow editing of label map segmentations when panelSegmentation.dis
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.labelMapSegLocking.globalUnlockedSegPostEdit
);
});
+6 -1
View File
@@ -19,5 +19,10 @@ test('should display the length tool', async ({
{ x: 544, y: 232 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.length.lengthDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.length.lengthDisplayedCorrectly
);
});
+25 -2
View File
@@ -1,4 +1,11 @@
import { checkForScreenshot, screenShotPaths, test, visitStudy, expect } from './utils';
import {
checkForScreenshot,
expect,
getAnnotationStats,
screenShotPaths,
test,
visitStudy,
} from './utils';
import { press } from './utils/keyboardUtils';
test.beforeEach(async ({ page }) => {
@@ -24,7 +31,23 @@ test('should display the livewire tool', async ({
{ x: 383, y: 461 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.livewire.livewireDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.livewire.livewireDisplayedCorrectly
);
const livewires = await getAnnotationStats(page, { toolName: 'LivewireContour' });
expect(livewires.length).toBeGreaterThan(0);
const stats = livewires[0].firstTargetStats!;
expect(stats.areaUnit).toBe('mm²');
expect(Math.round(stats.area as number)).toBe(28906);
const lines = activeViewport.getSvgAnnotationStatTextLines(livewires[0].annotationUID);
await expect(lines).toHaveCount(1);
await expect(lines.nth(0)).toHaveText('Area: 28906 mm²');
});
test('should restore viewport interactivity after deleting an in-progress Livewire annotation via context menu', async ({
+11 -2
View File
@@ -7,8 +7,17 @@ test.beforeEach(async ({ page }) => {
});
test.describe('MPR Test', async () => {
test('should render MPR correctly.', async ({ page, mainToolbarPageObject }) => {
test('should render MPR correctly.', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.layoutSelection.MPR.click();
await checkForScreenshot(page, page, screenShotPaths.mpr.mprDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.mpr.mprDisplayedCorrectly
);
});
});
+2 -1
View File
@@ -25,7 +25,7 @@ test('should launch MPR with unhydrated RTSTRUCT chosen from the data overlay me
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.mprThenRTOverlayNoHydration.mprPreRTOverlayNoHydration
);
@@ -48,6 +48,7 @@ test('should launch MPR with unhydrated RTSTRUCT chosen from the data overlay me
await checkForScreenshot({
page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.mprThenRTOverlayNoHydration.mprPostRTOverlayNoHydration,
normalizedClip: { x: 0, y: 0, width: 1.0, height: 0.75 }, // clip to avoid any popups concerning surface creation and clipping
});
+2 -2
View File
@@ -21,7 +21,7 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu',
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.mprThenSEGOverlayNoHydration.mprPreSEGOverlayNoHydration
);
@@ -42,7 +42,7 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu',
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.mprThenSEGOverlayNoHydration.mprPostSEGOverlayNoHydration
);
});
+6 -6
View File
@@ -69,15 +69,15 @@ test('checks if measurement item can be relabeled under Measurements panel', asy
// Open measurement panel confirm default empty
await rightPanelPageObject.measurementsPanel.select();
const measurementRow = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0).locator;
await expect(measurementRow).toContainText('(empty)');
const measurementRow = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0);
await expect(measurementRow.title).toHaveText('(empty)');
// Expand and click rename
await rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0).actions.rename(relabelText);
// Check dialog closed and renamed
await expect(DOMOverlayPageObject.dialog.input.locator).toBeHidden();
await expect(measurementRow).toContainText(relabelText);
await expect(measurementRow.title).toHaveText(relabelText);
});
test('checks if measurement item can be relabeled through the context menu on the viewport', async ({
@@ -98,8 +98,8 @@ test('checks if measurement item can be relabeled through the context menu on th
// Open measurement panel confirm default empty
await rightPanelPageObject.measurementsPanel.select();
const measurementRow = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0).locator;
await expect(measurementRow).toContainText('(empty)');
const measurementRow = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0);
await expect(measurementRow.title).toHaveText('(empty)');
// Right click and click rename
await page.waitForTimeout(200); // small delay for context menu
@@ -117,7 +117,7 @@ test('checks if measurement item can be relabeled through the context menu on th
// Check dialog closed and renamed
await expect(DOMOverlayPageObject.dialog.title).toBeHidden();
await expect(measurementRow).toContainText(relabelText);
await expect(measurementRow.title).toHaveText(relabelText);
});
test('checks if image would jump when clicked on a measurement item', async ({
@@ -49,6 +49,7 @@ test('should display multiple segmentation overlays (both SEG and RT)', async ({
await checkForScreenshot({
page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.multipleSegmentationDataOverlays.threeSegOverlaysInOverlayMenu,
});
@@ -58,6 +59,7 @@ test('should display multiple segmentation overlays (both SEG and RT)', async ({
await checkForScreenshot({
page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.multipleSegmentationDataOverlays.threeSegOverlaysInOverlayMenu,
});
@@ -70,6 +72,7 @@ test('should display multiple segmentation overlays (both SEG and RT)', async ({
await checkForScreenshot({
page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.multipleSegmentationDataOverlays.overlaysDisplayed,
});
@@ -85,6 +88,7 @@ test('should display multiple segmentation overlays (both SEG and RT)', async ({
await checkForScreenshot({
page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.multipleSegmentationDataOverlays.overlaySEGsAndRTDisplayed,
});
@@ -94,6 +98,7 @@ test('should display multiple segmentation overlays (both SEG and RT)', async ({
await checkForScreenshot({
page,
locator: viewportPageObject.grid,
screenshotPath: screenShotPaths.multipleSegmentationDataOverlays.overlaySEGsAndRTDisplayed,
});
});
+6 -1
View File
@@ -16,5 +16,10 @@ test('should display the probe tool', async ({
const activeViewport = await viewportPageObject.active;
await activeViewport.clickAt([{ x: 550, y: 200 }]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.probe.probeDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.probe.probeDisplayedCorrectly
);
});
@@ -12,7 +12,8 @@ test('should overlay an unhydrated RTSTRUCT over a display set that the RTSTRUCT
page,
viewportPageObject,
}) => {
const dataOverlayPageObject = (await viewportPageObject.getById('default')).overlayMenu.dataOverlay;
const dataOverlayPageObject = (await viewportPageObject.getById('default')).overlayMenu
.dataOverlay;
await dataOverlayPageObject.toggle();
await dataOverlayPageObject.addSegmentation('Contours on PET');
@@ -26,7 +27,7 @@ test('should overlay an unhydrated RTSTRUCT over a display set that the RTSTRUCT
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.rtDataOverlayForUnreferencedDisplaySetNoHydration.overlayFirstImage
);
@@ -37,7 +38,7 @@ test('should overlay an unhydrated RTSTRUCT over a display set that the RTSTRUCT
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.rtDataOverlayForUnreferencedDisplaySetNoHydration.overlayMiddleImage
);
});
@@ -37,7 +37,7 @@ test('should launch MPR with unhydrated RTSTRUCT chosen from the data overlay me
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.rtDataOverlayNoHydrationThenMPR.rtDataOverlayNoHydrationPreMPR
);
@@ -47,7 +47,7 @@ test('should launch MPR with unhydrated RTSTRUCT chosen from the data overlay me
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.rtDataOverlayNoHydrationThenMPR.rtDataOverlayNoHydrationPostMPR
);
+16 -3
View File
@@ -11,17 +11,30 @@ test('should hydrate RT reports correctly', async ({
DOMOverlayPageObject,
leftPanelPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await leftPanelPageObject.loadSeriesByModality('RTSTRUCT');
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtPreHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydration.rtPreHydration
);
await DOMOverlayPageObject.viewport.segmentationHydration.yes.click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtPostHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydration.rtPostHydration
);
await rightPanelPageObject.labelMapSegmentationPanel.panel.segmentByText('Small Sphere').click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtJumpToStructure);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydration.rtJumpToStructure
);
});
+11 -2
View File
@@ -11,11 +11,16 @@ test('should hydrate RT reports correctly', async ({
DOMOverlayPageObject,
leftPanelPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await leftPanelPageObject.loadSeriesByModality('RTSTRUCT');
await checkForScreenshot(page, page, screenShotPaths.rtHydration2.rtPreHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydration2.rtPreHydration
);
// wait for 3 seconds
await page.evaluate(() => {
// Access cornerstone directly from the window object
@@ -40,5 +45,9 @@ test('should hydrate RT reports correctly', async ({
// should preserve zoom and pan and scroll position after hydration
await DOMOverlayPageObject.viewport.segmentationHydration.yes.click();
await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtPostHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydration.rtPostHydration
);
});
+17 -4
View File
@@ -12,6 +12,7 @@ test('should hydrate an RTSTRUCT from MPR', async ({
leftPanelPageObject,
mainToolbarPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
@@ -19,25 +20,37 @@ test('should hydrate an RTSTRUCT from MPR', async ({
await page.waitForTimeout(10000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationFromMPR.mprBeforeRT);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydrationFromMPR.mprBeforeRT
);
await leftPanelPageObject.loadSeriesByModality('RTSTRUCT');
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationFromMPR.mprAfterRT);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydrationFromMPR.mprAfterRT
);
await DOMOverlayPageObject.viewport.segmentationHydration.yes.click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationFromMPR.mprAfterRTHydrated);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydrationFromMPR.mprAfterRTHydrated
);
await mainToolbarPageObject.layoutSelection.axialPrimary.click();
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.rtHydrationFromMPR.mprAfterRTHydratedAfterLayoutChange
);
});
+7 -2
View File
@@ -12,6 +12,7 @@ test('should hydrate an RTSTRUCT and then launch MPR', async ({
leftPanelPageObject,
mainToolbarPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await leftPanelPageObject.loadSeriesByModality('RTSTRUCT');
@@ -20,7 +21,11 @@ test('should hydrate an RTSTRUCT and then launch MPR', async ({
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationThenMPR.rtPostHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtHydrationThenMPR.rtPostHydration
);
await mainToolbarPageObject.layoutSelection.axialPrimary.click();
@@ -28,7 +33,7 @@ test('should hydrate an RTSTRUCT and then launch MPR', async ({
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.rtHydrationThenMPR.rtPostHydrationMPRAxialPrimary
);
});
+11 -2
View File
@@ -11,17 +11,26 @@ test('should launch MPR with unhydrated RTSTRUCT', async ({
leftPanelPageObject,
mainToolbarPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await leftPanelPageObject.loadSeriesByModality('RTSTRUCT');
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtNoHydrationThenMPR.rtNoHydrationPreMPR);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtNoHydrationThenMPR.rtNoHydrationPreMPR
);
await mainToolbarPageObject.layoutSelection.MPR.click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtNoHydrationThenMPR.rtNoHydrationPostMPR);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rtNoHydrationThenMPR.rtNoHydrationPostMPR
);
});
+6 -1
View File
@@ -19,5 +19,10 @@ test('should display the rectangle tool', async ({
{ x: 591, y: 217 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.rectangle.rectangleDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rectangle.rectangleDisplayedCorrectly
);
});
+11 -2
View File
@@ -6,9 +6,18 @@ test.beforeEach(async ({ page }) => {
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should reset the image to its original state', async ({ page, mainToolbarPageObject }) => {
test('should reset the image to its original state', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.moreTools.rotateRight.click();
await mainToolbarPageObject.moreTools.invert.click();
await mainToolbarPageObject.moreTools.reset.click();
await checkForScreenshot(page, page, screenShotPaths.reset.resetDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.reset.resetDisplayedCorrectly
);
});
+11 -2
View File
@@ -6,7 +6,16 @@ test.beforeEach(async ({ page }) => {
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should rotate the image to the right', async ({ page, mainToolbarPageObject }) => {
test('should rotate the image to the right', async ({
page,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.moreTools.rotateRight.click();
await checkForScreenshot(page, page, screenShotPaths.rotateRight.rotateRightDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.rotateRight.rotateRightDisplayedCorrectly
);
});
@@ -14,7 +14,8 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu',
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
const dataOverlayPageObject = (await viewportPageObject.getById('default')).overlayMenu.dataOverlay;
const dataOverlayPageObject = (await viewportPageObject.getById('default')).overlayMenu
.dataOverlay;
await dataOverlayPageObject.toggle();
await dataOverlayPageObject.addSegmentation('Segmentation');
@@ -28,7 +29,7 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu',
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.segDataOverlayNoHydrationThenMPR.segDataOverlayNoHydrationPreMPR
);
@@ -38,7 +39,7 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu',
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.segDataOverlayNoHydrationThenMPR.segDataOverlayNoHydrationPostMPR
);
+11 -2
View File
@@ -11,12 +11,17 @@ test('should hydrate SEG reports correctly', async ({
DOMOverlayPageObject,
leftPanelPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await leftPanelPageObject.loadSeriesByDescription('SEG');
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPreHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.segHydration.segPreHydration
);
await page.evaluate(() => {
// Access cornerstone directly from the window object
@@ -40,5 +45,9 @@ test('should hydrate SEG reports correctly', async ({
await DOMOverlayPageObject.viewport.segmentationHydration.yes.click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPostHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.segHydration.segPostHydration
);
});
+3 -2
View File
@@ -35,9 +35,10 @@ test.describe('3D four up SEG hydration', async () => {
screenShotPaths.segHydrationFrom3DFourUp.threeDFourUpBeforeSEG
);
let viewportRenderCycle = waitForViewportRenderCycle(page);
await leftPanelPageObject.loadSeriesByDescription('SEG');
await waitForViewportsRendered(page);
await viewportRenderCycle;
await checkForScreenshot(
page,
@@ -49,7 +50,7 @@ test.describe('3D four up SEG hydration', async () => {
// High rendered timeout needed: layout has 4 viewports (3D volume + MPR planes + SEG overlays),
// which can take significantly longer time to fully render
const viewportRenderCycle = waitForViewportRenderCycle(page, { renderedTimeout: 180000 });
viewportRenderCycle = waitForViewportRenderCycle(page, { renderedTimeout: 180000 });
await DOMOverlayPageObject.viewport.segmentationHydration.yes.click();
+7 -2
View File
@@ -12,6 +12,7 @@ test('should properly display MPR for MR', async ({
leftPanelPageObject,
mainToolbarPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await leftPanelPageObject.loadSeriesByDescription('SEG');
@@ -19,14 +20,18 @@ test('should properly display MPR for MR', async ({
await DOMOverlayPageObject.viewport.segmentationHydration.yes.click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydrationThenMPR.segPostHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.segHydrationThenMPR.segPostHydration
);
await mainToolbarPageObject.layoutSelection.axialPrimary.click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
viewportPageObject.grid,
screenShotPaths.segHydrationThenMPR.segPostHydrationMPRAxialPrimary
);
});
+11 -2
View File
@@ -11,17 +11,26 @@ test('should launch MPR with unhydrated SEG', async ({
leftPanelPageObject,
mainToolbarPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await leftPanelPageObject.loadSeriesByDescription('SEG');
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segNoHydrationThenMPR.segNoHydrationPreMPR);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.segNoHydrationThenMPR.segNoHydrationPreMPR
);
await mainToolbarPageObject.layoutSelection.MPR.click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segNoHydrationThenMPR.segNoHydrationPostMPR);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.segNoHydrationThenMPR.segNoHydrationPostMPR
);
});
+16 -3
View File
@@ -11,12 +11,17 @@ test('should hydrate SR reports correctly', async ({
DOMOverlayPageObject,
leftPanelPageObject,
rightPanelPageObject,
viewportPageObject,
}) => {
await rightPanelPageObject.toggle();
await rightPanelPageObject.measurementsPanel.select();
await leftPanelPageObject.loadSeriesByModality('SR');
await page.waitForTimeout(2000);
await checkForScreenshot(page, page, screenShotPaths.srHydration.srPreHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.srHydration.srPreHydration
);
await page.evaluate(() => {
// Access cornerstone directly from the window object
@@ -39,7 +44,11 @@ test('should hydrate SR reports correctly', async ({
await DOMOverlayPageObject.viewport.segmentationHydration.yes.click();
await page.waitForTimeout(2000);
await checkForScreenshot(page, page, screenShotPaths.srHydration.srPostHydration);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.srHydration.srPostHydration
);
await page.evaluate(() => {
// Access cornerstone directly from the window object
@@ -62,5 +71,9 @@ test('should hydrate SR reports correctly', async ({
await rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0).click();
await checkForScreenshot(page, page, screenShotPaths.srHydration.srJumpToMeasurement);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.srHydration.srJumpToMeasurement
);
});
+17 -16
View File
@@ -1,5 +1,14 @@
import { Locator } from '@playwright/test';
import { checkForScreenshot, expect, screenShotPaths, test, visitStudy } from './utils';
async function expectNonEmptyDetailLines(lines: Locator) {
const lineCount = await lines.count();
expect(lineCount).toBeGreaterThan(0);
for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
await expect(lines.nth(lineIndex)).not.toBeEmpty();
}
}
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.7310.5101.860473186348887719777907797922';
const mode = 'viewer';
@@ -92,19 +101,13 @@ test('should hydrate SCOORD3D probe measurements correctly', async ({
);
// Verify the measurements list has the correct probe measurements
const measurementRows = page.getByTestId('data-row');
const rowCount = await measurementRows.count();
expect(await rightPanelPageObject.measurementsPanel.panel.getMeasurementCount()).toBeGreaterThan(
0
);
expect(await rightPanelPageObject.measurementsPanel.panel.rows).not.toHaveCount(0);
const rowCount = await rightPanelPageObject.measurementsPanel.panel.getMeasurementCount();
// Verify that the measurements are probe measurements (not other types)
for (let i = 0; i < rowCount; i++) {
const measurementText = await rightPanelPageObject.measurementsPanel.panel
.nthMeasurement(i)
.locator.textContent();
// Probe measurements should be present, verify they're not other measurement types
expect(measurementText).toBeTruthy();
const measurement = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(i);
await expect(measurement.title).not.toBeEmpty();
await expectNonEmptyDetailLines(measurement.stats.primary.lines);
}
// Test jumping to a specific measurement by scrolling and clicking
@@ -199,10 +202,8 @@ test('should display SCOORD3D probe measurements correctly', async ({
// Verify that the measurements are probe measurements (not other types like rectangle)
for (let i = 0; i < rowCount; i++) {
const measurementText = await rightPanelPageObject.measurementsPanel.panel
.nthMeasurement(i)
.locator.textContent();
// Probe measurements should be present, verify they're not other measurement types
expect(measurementText).toBeTruthy();
const measurement = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(i);
await expect(measurement.title).not.toBeEmpty();
await expectNonEmptyDetailLines(measurement.stats.primary.lines);
}
});
+54 -13
View File
@@ -1,4 +1,21 @@
import { checkForScreenshot, expect, screenShotPaths, test, visitStudy } from './utils';
import type { Locator } from '@playwright/test';
import {
checkForScreenshot,
expect,
getAnnotationStats,
screenShotPaths,
test,
visitStudy,
} from './utils';
async function expectNonEmptyDetailLines(lines: Locator) {
const lineCount = await lines.count();
expect(lineCount).toBeGreaterThan(0);
for (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {
await expect(lines.nth(lineIndex)).not.toBeEmpty();
}
}
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.2.840.113654.2.55.242841386983064378162007136685545369722';
@@ -96,13 +113,11 @@ test('should hydrate SCOORD rectangle measurements correctly', async ({
const rowCount = await rightPanelPageObject.measurementsPanel.panel.getMeasurementCount();
expect(rowCount).toBeGreaterThan(0);
// Verify that the measurements are rectangle measurements (not other types)
for (let i = 0; i < rowCount; i++) {
const measurementText = await rightPanelPageObject.measurementsPanel.panel
.nthMeasurement(i)
.locator.textContent();
// Rectangle measurements should be present, verify they're not other measurement types
expect(measurementText).toBeTruthy();
const measurement = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(i);
await expect(measurement.title).not.toBeEmpty();
await expectNonEmptyDetailLines(measurement.stats.primary.lines);
await expectNonEmptyDetailLines(measurement.stats.secondary.lines);
}
// Test jumping to a specific measurement by scrolling and clicking
@@ -195,12 +210,38 @@ test('should display SCOORD rectangle measurements correctly', async ({
const rowCount = await rightPanelPageObject.measurementsPanel.panel.getMeasurementCount();
expect(rowCount).toBeGreaterThan(0);
// Verify that the measurements are rectangle measurements (not other types like probe)
const firstMeasurement = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(0);
const expectedFirstPrimaryDetailLines = ['276 mm²', 'Max: 598 HU', 'Lung structure'];
await expect(firstMeasurement.stats.primary.lines).toHaveCount(
expectedFirstPrimaryDetailLines.length
);
for (let lineIndex = 0; lineIndex < expectedFirstPrimaryDetailLines.length; lineIndex++) {
await expect(firstMeasurement.stats.primary.lines.nth(lineIndex)).toHaveText(
expectedFirstPrimaryDetailLines[lineIndex]
);
}
// Assert against the source-of-truth cachedStats on the cornerstone annotation,
// independent of UI label formatting. Values should match the displayed labels
// after rounding ("276 mm²", "Max: 598 HU").
const rectangles = await getAnnotationStats(page, { toolName: 'RectangleROI' });
expect(rectangles.length).toBeGreaterThan(0);
const stats = rectangles[0].firstTargetStats!;
expect(stats.areaUnit).toBe('mm²');
expect(Math.round(stats.area as number)).toBe(276);
expect(Math.round(stats.max as number)).toBe(598);
expect(stats.Modality).toBe('CT');
expect(stats.modalityUnit).toBe('HU');
const lines = activeViewport.getSvgAnnotationStatTextLines(rectangles[0].annotationUID);
await expect(lines).toHaveCount(5);
await expect(lines.nth(0)).toHaveText('Area: 276 mm²');
for (let i = 0; i < rowCount; i++) {
const measurementText = await rightPanelPageObject.measurementsPanel.panel
.nthMeasurement(i)
.locator.textContent();
// Rectangle measurements should be present, verify they're not other measurement types
expect(measurementText).toBeTruthy();
const measurement = rightPanelPageObject.measurementsPanel.panel.nthMeasurement(i);
await expect(measurement.title).not.toBeEmpty();
await expectNonEmptyDetailLines(measurement.stats.primary.lines);
await expectNonEmptyDetailLines(measurement.stats.secondary.lines);
}
});
+24 -2
View File
@@ -1,4 +1,11 @@
import { checkForScreenshot, screenShotPaths, test, visitStudy, expect } from './utils';
import {
checkForScreenshot,
expect,
getAnnotationStats,
screenShotPaths,
test,
visitStudy,
} from './utils';
import { press } from './utils/keyboardUtils';
test.beforeEach(async ({ page }) => {
@@ -24,7 +31,22 @@ test('should display the spline tool', async ({
{ x: 383, y: 461 },
]);
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
await checkForScreenshot(page, page, screenShotPaths.spline.splineDisplayedCorrectly);
await checkForScreenshot(
page,
viewportPageObject.grid,
screenShotPaths.spline.splineDisplayedCorrectly
);
const splines = await getAnnotationStats(page, { toolName: 'SplineROI' });
expect(splines.length).toBeGreaterThan(0);
const stats = splines[0].firstTargetStats!;
expect(stats.areaUnit).toBe('mm²');
expect(Math.round(stats.area as number)).toBe(38811);
const lines = activeViewport.getSvgAnnotationStatTextLines(splines[0].annotationUID);
await expect(lines).toHaveCount(1);
await expect(lines.nth(0)).toHaveText('Area: 38811 mm²');
});
test('should restore viewport interactivity after deleting an in-progress Spline annotation via context menu', async ({
+40 -16
View File
@@ -112,30 +112,58 @@ export class RightPanelPageObject {
get measurementsPanel() {
const page = this.page;
const getMeasurementByIdx = (index: number) => this.getPanelRowByIdx(index);
const getMeasurementByText = (text: string) => this.getPanelRowByText(text);
const menuButton = page.getByTestId('trackedMeasurements-btn');
const trackedMeasurementsPanel = page.getByTestId('trackedMeasurements-panel').last();
const measurementTableRows = trackedMeasurementsPanel.locator(
'[data-cy^="measurement-table-row-"]'
);
const getMeasurementStatsAt = (index: number) => {
const dataRow = trackedMeasurementsPanel
.getByTestId(`measurement-table-row-${index}`)
.getByTestId('data-row');
const locator = dataRow.locator('..').getByTestId('data-row-details');
const primaryLocator = locator.getByTestId('data-row-details-primary');
const secondaryLocator = locator.getByTestId('data-row-details-secondary');
return {
locator,
primary: {
locator: primaryLocator,
lines: primaryLocator.getByTestId('data-row-detail-line'),
},
secondary: {
locator: secondaryLocator,
lines: secondaryLocator.getByTestId('data-row-detail-line'),
},
};
};
const getMeasurementDataRowAt = (index: number) => {
const rowWrapper = trackedMeasurementsPanel.getByTestId(`measurement-table-row-${index}`);
const dataRow = rowWrapper.getByTestId('data-row');
const row = this.getPanelRowDataObject(rowWrapper);
// Selection highlight (bg-popover) is on the inner DataRow, not the measurement-table-row wrapper
return {
...row,
locator: dataRow,
stats: getMeasurementStatsAt(index),
};
};
const trackedMeasurementsMenu = page.getByTestId('trackedMeasurements-btn');
return {
menuButton,
panel: {
deleteAll: async () => {
await page.getByRole('button', { name: 'Delete' }).click();
},
getMeasurementCount: async () => {
return await page.getByTestId('data-row').count();
return await measurementTableRows.count();
},
rows: page.getByTestId('data-row'),
locator: page.getByTestId('trackedMeasurements-panel').last(),
rows: measurementTableRows,
locator: trackedMeasurementsPanel,
nthMeasurement(index: number) {
return getMeasurementByIdx(index);
},
measurementByText(text: string) {
return getMeasurementByText(text);
return getMeasurementDataRowAt(index);
},
},
select: async () => {
await menuButton.click();
await trackedMeasurementsMenu.click();
},
};
}
@@ -414,7 +442,6 @@ export class RightPanelPageObject {
get microscopyPanel() {
const page = this.page;
const getMeasurementByIdx = (index: number) => this.getPanelRowByIdx(index);
const getMeasurementByText = (text: string) => this.getPanelRowByText(text);
return {
locator: page.getByTestId('measurements-panel'),
@@ -424,9 +451,6 @@ export class RightPanelPageObject {
nthMeasurement(index: number) {
return getMeasurementByIdx(index);
},
measurementByText(text: string) {
return getMeasurementByText(text);
},
};
}
}
+23 -1
View File
@@ -5,12 +5,13 @@ import {
simulateDoubleClickOnElement,
simulateNormalizedClicksOnElement,
simulateNormalizedDragOnElement,
simulateNormalizedPathDragOnElement,
} from '../utils';
import { DataOverlayPageObject } from './DataOverlayPageObject';
import { DOMOverlayPageObject } from './DOMOverlayPageObject';
import { MagnifyGlassPageObject } from './MagnifyGlassPageObject';
type SvgInnerElement = 'circle' | 'path' | 'd' | 'line';
type SvgInnerElement = 'circle' | 'path' | 'd' | 'line' | 'g';
type NormalizedDragParams = {
start: { x: number; y: number };
@@ -18,6 +19,11 @@ type NormalizedDragParams = {
config?: { button?: 'left' | 'right' | 'middle'; delay?: number; steps?: number };
};
type NormalizedPathDragParams = {
path: { x: number; y: number }[];
config?: { button?: 'left' | 'right' | 'middle'; delay?: number; steps?: number };
};
export interface IOverlayText {
get windowLevel(): Locator;
get instanceNumber(): Locator;
@@ -56,6 +62,7 @@ export interface IViewportPageObject {
button?: 'left' | 'right' | 'middle'
) => Promise<void>;
normalizedDragAt: (params: NormalizedDragParams) => Promise<void>;
normalizedPathDragAt: (params: NormalizedPathDragParams) => Promise<void>;
orientationMarkers: {
topMid: Locator;
leftMid: Locator;
@@ -81,6 +88,7 @@ export interface IViewportPageObject {
};
pane: Locator;
svg: (innerElement?: SvgInnerElement) => Locator;
getSvgAnnotationStatTextLines: (uid: string) => Locator;
navigationArrows: {
locator: Locator;
prev: {
@@ -297,6 +305,15 @@ export class ViewportPageObject {
steps: params.config?.steps,
});
},
normalizedPathDragAt: async (params: NormalizedPathDragParams) => {
await simulateNormalizedPathDragOnElement({
locator: viewport,
path: params.path,
button: params.config?.button,
delay: params.config?.delay,
steps: params.config?.steps,
});
},
orientationMarkers: this.getOrientationMarkers(viewport),
overlayText: this.getOverlayText(viewport),
overlayMenu: await this.getOverlayMenu(viewport),
@@ -304,6 +321,11 @@ export class ViewportPageObject {
svg: (innerElement?: SvgInnerElement) => {
return this.getSvg(viewport, innerElement);
},
getSvgAnnotationStatTextLines: (uid: string) => {
return this.getSvg(viewport)
.locator(`g[data-annotation-uid="${uid}"]`)
.locator('tspan');
},
navigationArrows: this.getNavigationArrows(viewport),
sliceNavigation: this.getSliceNavigation(viewport),
magnifyGlass: new MagnifyGlassPageObject(this.page, viewport),
Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Loaded 100 of 174 files, more files were not shown because too many files have changed in this diff. Show more