fix(segmentation): Added support for unhydrated RTSTRUCT in MPR. Added support for overlaying segmentations over display sets the segmentations do not reference. (#5168)

This commit is contained in:
Joe Boccanfuso authored and GitHub committed 2025-07-02 08:38:22 -04:00
1 parent 77758fde4f
commit 4f6f5254fb
28 files changed
+320 -4

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
import { test } from 'playwright-test-coverage';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should launch MPR with unhydrated RTSTRUCT chosen from the data overlay menu', async ({
page,
}) => {
await page.getByTestId('side-panel-header-right').click();
await page.getByTestId('Layout').click();
await page.getByTestId('MPR').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.mprThenRTOverlayNoHydration.mprPreRTOverlayNoHydration
);
// Hover over the middle/sagittal viewport so that the data overlay menu is available.
await page.locator('css=div[data-viewportid="mpr-sagittal"]').hover();
await page.getByTestId('dataOverlayMenu-mpr-sagittal-btn').click();
await page.getByTestId('AddSegmentationDataOverlay-mpr-sagittal').click();
await page.getByText('SELECT A SEGMENTATION').click();
await page.getByTestId('ARIA RadOnc Structure Sets').click();
// Hide the overlay menu.
await page.getByTestId('dataOverlayMenu-mpr-sagittal-btn').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.mprThenRTOverlayNoHydration.mprPostRTOverlayNoHydration
);
});
@@ -0,0 +1,38 @@
import { test } from 'playwright-test-coverage';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
import { press } from './utils/keyboardUtils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.2.840.113619.2.290.3.3767434740.226.1600859119.501';
const mode = 'segmentation';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should overlay an unhydrated RTSTRUCT over a display set that the RTSTRUCT does NOT reference', async ({
page,
}) => {
await page.getByTestId('dataOverlayMenu-default-btn').click();
await page.getByTestId('AddSegmentationDataOverlay-default').click();
await page.getByText('SELECT A SEGMENTATION').click();
await page.getByTestId('Contours on PET').click();
// Hide the overlay menu.
await page.getByTestId('dataOverlayMenu-default-btn').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.rtDataOverlayForUnreferencedDisplaySetNoHydration.overlayFirstImage
);
// Navigate to the middle image of the default viewport.
await press({ page, key: 'ArrowDown', nTimes: 23 });
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.rtDataOverlayForUnreferencedDisplaySetNoHydration.overlayMiddleImage
);
});
@@ -0,0 +1,38 @@
import { test } from 'playwright-test-coverage';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should launch MPR with unhydrated RTSTRUCT chosen from the data overlay menu', async ({
page,
}) => {
await page.getByTestId('side-panel-header-right').click();
await page.getByTestId('dataOverlayMenu-default-btn').click();
await page.getByTestId('AddSegmentationDataOverlay-default').click();
await page.getByText('SELECT A SEGMENTATION').click();
await page.getByTestId('ARIA RadOnc Structure Sets').click();
// Hide the overlay menu.
await page.getByTestId('dataOverlayMenu-default-btn').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.rtDataOverlayNoHydrationThenMPR.rtDataOverlayNoHydrationPreMPR
);
await page.getByTestId('Layout').click();
await page.getByTestId('MPR').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.rtDataOverlayNoHydrationThenMPR.rtDataOverlayNoHydrationPostMPR
);
});
+42
View File
@@ -0,0 +1,42 @@
import { test } from 'playwright-test-coverage';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should hydrate an RTSTRUCT from MPR', async ({ page }) => {
await page.getByTestId('side-panel-header-right').click();
await page.getByTestId('Layout').click();
await page.getByTestId('MPR').click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationFromMPR.mprBeforeRT);
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationFromMPR.mprAfterRT);
await page.getByTestId('yes-hydrate-btn').click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationFromMPR.mprAfterRTHydrated);
await page.getByTestId('Layout').click();
await page.getByTestId('Axial Primary').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.rtHydrationFromMPR.mprAfterRTHydratedAfterLayoutChange
);
});
+28
View File
@@ -0,0 +1,28 @@
import { test } from 'playwright-test-coverage';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should hydrate an RTSTRUCT and then launch MPR', async ({ page }) => {
await page.getByTestId('side-panel-header-right').click();
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
await page.getByTestId('yes-hydrate-btn').click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtHydrationThenMPR.rtPostHydration);
await page.getByTestId('Layout').click();
await page.getByTestId('Axial Primary').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.rtHydrationThenMPR.rtPostHydrationMPRAxialPrimary
);
});
+22
View File
@@ -0,0 +1,22 @@
import { test } from 'playwright-test-coverage';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should launch MPR with unhydrated RTSTRUCT', async ({ page }) => {
await page.getByTestId('side-panel-header-right').click();
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtNoHydrationThenMPR.rtNoHydrationPreMPR);
await page.getByTestId('Layout').click();
await page.getByTestId('MPR').click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.rtNoHydrationThenMPR.rtNoHydrationPostMPR);
});
@@ -0,0 +1,38 @@
import { test } from 'playwright-test-coverage';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
import { press } from './utils/keyboardUtils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785';
const mode = 'segmentation';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should overlay an unhydrated SEG over a display set that the SEG does NOT reference', async ({
page,
}) => {
await page.getByTestId('dataOverlayMenu-default-btn').click();
await page.getByTestId('AddSegmentationDataOverlay-default').click();
await page.getByText('SELECT A SEGMENTATION').click();
await page.getByTestId('Segmentation').click();
// Hide the overlay menu.
await page.getByTestId('dataOverlayMenu-default-btn').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.segDataOverlayForUnreferencedDisplaySetNoHydration.overlayFirstImage
);
// Navigate to the middle image of the default viewport.
await press({ page, key: 'ArrowDown', nTimes: 9 });
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.segDataOverlayForUnreferencedDisplaySetNoHydration.overlayMiddleImage
);
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

+26
View File
@@ -0,0 +1,26 @@
import { Page } from '@playwright/test';
/**
* PressParams defines the parameters for the press function.
* @property {Page} page - The Playwright page on which to perform the key press.
* @property {string} key - The key to be pressed.
* @property {number} [nTimes=1] - The number of times to press the key (default is 1).
* @property {unknown} [options={}] - Additional options for the key press.
*/
type PressParams = {
page: Page;
key: string;
nTimes?: number;
options?: unknown;
};
/**
* Convenience function to press a key multiple times on a Playwright page.
*/
export const press = async (params: PressParams) => {
const { page, key, nTimes = 1, options = {} } = params;
for (let i = 0; i < nTimes; i += 1) {
await page.keyboard.press(key, options);
}
};
+30
View File
@@ -69,6 +69,28 @@ const screenShotPaths = {
reset: {
resetDisplayedCorrectly: 'resetDisplayedCorrectly.png',
},
rtDataOverlayForUnreferencedDisplaySetNoHydration: {
overlayFirstImage: 'overlayFirstImage.png',
overlayMiddleImage: 'overlayMiddleImage.png',
},
rtDataOverlayNoHydrationThenMPR: {
rtDataOverlayNoHydrationPreMPR: 'rtDataOverlayNoHydrationPreMpr.png',
rtDataOverlayNoHydrationPostMPR: 'rtDataOverlayNoHydrationPostMpr.png',
},
rtHydrationFromMPR: {
mprBeforeRT: 'mprBeforeRT.png',
mprAfterRT: 'mprAfterRT.png',
mprAfterRTHydrated: 'mprAfterRTHydrated.png',
mprAfterRTHydratedAfterLayoutChange: 'mprAfterRTHydratedAfterLayoutChange.png',
},
rtHydrationThenMPR: {
rtPostHydration: 'rtPostHydration.png',
rtPostHydrationMPRAxialPrimary: 'rtPostHydrationMPRAxialPrimary.png',
},
rtNoHydrationThenMPR: {
rtNoHydrationPreMPR: 'rtNoHydrationPreMpr.png',
rtNoHydrationPostMPR: 'rtNoHydrationPostMpr.png',
},
srHydration: {
srPostHydration: 'srPostHydration.png',
srPreHydration: 'srPreHydration.png',
@@ -93,10 +115,18 @@ const screenShotPaths = {
segNoHydrationPreMPR: 'segNoHydrationPreMpr.png',
segNoHydrationPostMPR: 'segNoHydrationPostMpr.png',
},
segDataOverlayForUnreferencedDisplaySetNoHydration: {
overlayFirstImage: 'overlayFirstImage.png',
overlayMiddleImage: 'overlayMiddleImage.png',
},
segDataOverlayNoHydrationThenMPR: {
segDataOverlayNoHydrationPreMPR: 'segDataOverlayNoHydrationPreMpr.png',
segDataOverlayNoHydrationPostMPR: 'segDataOverlayNoHydrationPostMpr.png',
},
mprThenRTOverlayNoHydration: {
mprPreRTOverlayNoHydration: 'mprPreRTOverlayNoHydration.png',
mprPostRTOverlayNoHydration: 'mprPostRTOverlayNoHydration.png',
},
mprThenSEGOverlayNoHydration: {
mprPreSEGOverlayNoHydration: 'mprPreSEGOverlayNoHydration.png',
mprPostSEGOverlayNoHydration: 'mprPostSEGOverlayNoHydration.png',