fix(sr): sr hydration and load was not working, Screenshot Comparison, and Testing (#4814)
@ -140,19 +140,16 @@ function commandsModule({
|
|||||||
)?.[0];
|
)?.[0];
|
||||||
|
|
||||||
if (previousReferencedDisplaySetStoreKey) {
|
if (previousReferencedDisplaySetStoreKey) {
|
||||||
if (referencedImageId) {
|
const presentationData = referencedImageId
|
||||||
setPositionPresentation(previousReferencedDisplaySetStoreKey, {
|
? {
|
||||||
viewReference: {
|
...presentations.positionPresentation,
|
||||||
referencedImageId,
|
viewReference: {
|
||||||
},
|
referencedImageId,
|
||||||
});
|
},
|
||||||
} else {
|
}
|
||||||
setPositionPresentation(
|
: presentations.positionPresentation;
|
||||||
previousReferencedDisplaySetStoreKey,
|
|
||||||
presentations.positionPresentation
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
setPositionPresentation(previousReferencedDisplaySetStoreKey, presentationData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,9 +47,19 @@ const RowComponent = ({
|
|||||||
{isChildOrParent && (
|
{isChildOrParent && (
|
||||||
<div style={{ paddingLeft: `${padding}px`, opacity: onToggle ? 1 : 0 }}>
|
<div style={{ paddingLeft: `${padding}px`, opacity: onToggle ? 1 : 0 }}>
|
||||||
{row.areChildrenVisible ? (
|
{row.areChildrenVisible ? (
|
||||||
<Icons.ChevronDown onClick={handleToggle} />
|
<div
|
||||||
|
className="cursor-pointer p-1"
|
||||||
|
onClick={handleToggle}
|
||||||
|
>
|
||||||
|
<Icons.ChevronDown />
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Icons.ChevronRight onClick={handleToggle} />
|
<div
|
||||||
|
className="cursor-pointer p-1"
|
||||||
|
onClick={handleToggle}
|
||||||
|
>
|
||||||
|
<Icons.ChevronRight />
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -88,7 +88,8 @@ function TrackedMeasurementsContextProvider(
|
|||||||
m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID)
|
m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID)
|
||||||
);
|
);
|
||||||
|
|
||||||
const trackedMeasurement = trackedMeasurements[0];
|
// Jump to the last tracked measurement - most recent
|
||||||
|
const trackedMeasurement = trackedMeasurements[trackedMeasurements.length - 1];
|
||||||
const referencedDisplaySetUID = trackedMeasurement.displaySetInstanceUID;
|
const referencedDisplaySetUID = trackedMeasurement.displaySetInstanceUID;
|
||||||
|
|
||||||
// update the previously stored positionPresentation with the new viewportId
|
// update the previously stored positionPresentation with the new viewportId
|
||||||
@ -166,11 +167,13 @@ function TrackedMeasurementsContextProvider(
|
|||||||
hydrateStructuredReport: hydrateStructuredReport.bind(null, {
|
hydrateStructuredReport: hydrateStructuredReport.bind(null, {
|
||||||
servicesManager,
|
servicesManager,
|
||||||
extensionManager,
|
extensionManager,
|
||||||
|
commandsManager,
|
||||||
appConfig,
|
appConfig,
|
||||||
}),
|
}),
|
||||||
promptLabelAnnotation: promptLabelAnnotation.bind(null, {
|
promptLabelAnnotation: promptLabelAnnotation.bind(null, {
|
||||||
servicesManager,
|
servicesManager,
|
||||||
extensionManager,
|
extensionManager,
|
||||||
|
commandsManager,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
machineOptions.guards = Object.assign({}, machineOptions.guards, {
|
machineOptions.guards = Object.assign({}, machineOptions.guards, {
|
||||||
|
|||||||
@ -216,7 +216,7 @@ export default function PanelStudyBrowserTracking({
|
|||||||
thumbnailSrc = await displaySet.getThumbnailSrc();
|
thumbnailSrc = await displaySet.getThumbnailSrc();
|
||||||
}
|
}
|
||||||
if (!thumbnailSrc) {
|
if (!thumbnailSrc) {
|
||||||
let thumbnailSrc = await getImageSrc(imageId);
|
const thumbnailSrc = await getImageSrc(imageId);
|
||||||
displaySet.thumbnailSrc = thumbnailSrc;
|
displaySet.thumbnailSrc = thumbnailSrc;
|
||||||
}
|
}
|
||||||
newImageSrcEntry[dSet.displaySetInstanceUID] = thumbnailSrc;
|
newImageSrcEntry[dSet.displaySetInstanceUID] = thumbnailSrc;
|
||||||
|
|||||||
@ -57,6 +57,7 @@
|
|||||||
"test:e2e:ci": "npx playwright test",
|
"test:e2e:ci": "npx playwright test",
|
||||||
"test:e2e:ui": "npx playwright test --ui",
|
"test:e2e:ui": "npx playwright test --ui",
|
||||||
"test:e2e:headed": "npx playwright test --headed",
|
"test:e2e:headed": "npx playwright test --headed",
|
||||||
|
"test:e2e:debug": "npx playwright test --debug",
|
||||||
"test:e2e:dist": "lerna run test:e2e:dist --stream",
|
"test:e2e:dist": "lerna run test:e2e:dist --stream",
|
||||||
"test:e2e:serve": "yarn test:data && lerna run test:e2e:serve --stream",
|
"test:e2e:serve": "yarn test:data && lerna run test:e2e:serve --stream",
|
||||||
"see-changed": "lerna changed",
|
"see-changed": "lerna changed",
|
||||||
|
|||||||
42
tests/RTHydration2.spec.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { test } from '@playwright/test';
|
||||||
|
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 RT reports correctly', async ({ page }) => {
|
||||||
|
await page.getByTestId('side-panel-header-right').click();
|
||||||
|
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
|
||||||
|
|
||||||
|
await checkForScreenshot(page, page, screenShotPaths.rtHydration2.rtPreHydration);
|
||||||
|
// wait for 3 seconds
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// Access cornerstone directly from the window object
|
||||||
|
const cornerstone = window.cornerstone;
|
||||||
|
if (!cornerstone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabledElements = cornerstone.getEnabledElements();
|
||||||
|
if (enabledElements.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewport = enabledElements[0].viewport;
|
||||||
|
if (viewport) {
|
||||||
|
viewport.setZoom(4);
|
||||||
|
viewport.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(3000);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
// should preserve zoom and pan and scroll position after hydration
|
||||||
|
await page.getByTestId('yes-hydrate-btn').click();
|
||||||
|
await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtPostHydration);
|
||||||
|
});
|
||||||
@ -11,8 +11,26 @@ test('should hydrate SEG reports correctly', async ({ page }) => {
|
|||||||
await page.getByTestId('side-panel-header-right').click();
|
await page.getByTestId('side-panel-header-right').click();
|
||||||
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
|
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
|
||||||
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPreHydration);
|
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPreHydration);
|
||||||
|
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// Access cornerstone directly from the window object
|
||||||
|
const cornerstone = window.cornerstone;
|
||||||
|
if (!cornerstone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabledElements = cornerstone.getEnabledElements();
|
||||||
|
if (enabledElements.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewport = enabledElements[0].viewport;
|
||||||
|
if (viewport) {
|
||||||
|
viewport.setZoom(4);
|
||||||
|
viewport.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await page.getByTestId('yes-hydrate-btn').click();
|
await page.getByTestId('yes-hydrate-btn').click();
|
||||||
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPostHydration);
|
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPostHydration);
|
||||||
await page.getByText('Esophagus').click();
|
|
||||||
await checkForScreenshot(page, page, screenShotPaths.segHydration.segJumpToSegment);
|
|
||||||
});
|
});
|
||||||
|
|||||||
25
tests/SEGHydrationMPR.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { test } from '@playwright/test';
|
||||||
|
import { visitStudy, checkForScreenshot, screenShotPaths } 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 properly display MPR for MR', 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 checkForScreenshot(page, page, screenShotPaths.segHydrationMPR.segPostHydration);
|
||||||
|
|
||||||
|
await page.getByTestId('Layout').click();
|
||||||
|
await page.getByTestId('Axial Primary').click();
|
||||||
|
|
||||||
|
await checkForScreenshot(
|
||||||
|
page,
|
||||||
|
page,
|
||||||
|
screenShotPaths.segHydrationMPR.segPostHydrationMPRAxialPrimary
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -12,8 +12,48 @@ test('should hydrate SR reports correctly', async ({ page }) => {
|
|||||||
await page.getByTestId('trackedMeasurements-btn').click();
|
await page.getByTestId('trackedMeasurements-btn').click();
|
||||||
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
|
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
|
||||||
await checkForScreenshot(page, page, screenShotPaths.srHydration.srPreHydration);
|
await checkForScreenshot(page, page, screenShotPaths.srHydration.srPreHydration);
|
||||||
|
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// Access cornerstone directly from the window object
|
||||||
|
const cornerstone = window.cornerstone;
|
||||||
|
if (!cornerstone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabledElements = cornerstone.getEnabledElements();
|
||||||
|
if (enabledElements.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewport = enabledElements[0].viewport;
|
||||||
|
if (viewport) {
|
||||||
|
viewport.setZoom(4);
|
||||||
|
viewport.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await page.getByTestId('yes-hydrate-btn').click();
|
await page.getByTestId('yes-hydrate-btn').click();
|
||||||
await checkForScreenshot(page, page, screenShotPaths.srHydration.srPostHydration);
|
await checkForScreenshot(page, page, screenShotPaths.srHydration.srPostHydration);
|
||||||
|
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// Access cornerstone directly from the window object
|
||||||
|
const cornerstone = window.cornerstone;
|
||||||
|
if (!cornerstone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabledElements = cornerstone.getEnabledElements();
|
||||||
|
if (enabledElements.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewport = enabledElements[0].viewport;
|
||||||
|
if (viewport) {
|
||||||
|
viewport.scroll(20);
|
||||||
|
viewport.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await page.getByTestId('data-row').first().click();
|
await page.getByTestId('data-row').first().click();
|
||||||
await checkForScreenshot(page, page, screenShotPaths.srHydration.srJumpToMeasurement);
|
await checkForScreenshot(page, page, screenShotPaths.srHydration.srJumpToMeasurement);
|
||||||
});
|
});
|
||||||
|
|||||||
39
tests/mpr2.spec.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { test } from '@playwright/test';
|
||||||
|
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
const studyInstanceUID =
|
||||||
|
'1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046&hangingprotocolid=mpr';
|
||||||
|
const mode = 'viewer';
|
||||||
|
await visitStudy(page, studyInstanceUID, mode, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should properly display MPR for MR', async ({ page }) => {
|
||||||
|
await page.getByTestId('side-panel-header-right').click();
|
||||||
|
// await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
|
||||||
|
await checkForScreenshot(page, page, screenShotPaths.mpr2.mprDisplayedCorrectly);
|
||||||
|
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// Access cornerstone directly from the window object
|
||||||
|
const cornerstone = window.cornerstone;
|
||||||
|
if (!cornerstone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabledElements = cornerstone.getEnabledElements();
|
||||||
|
if (enabledElements.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply zoom to all viewports
|
||||||
|
for (let i = 0; i < enabledElements.length; i++) {
|
||||||
|
const viewport = enabledElements[i].viewport;
|
||||||
|
if (viewport) {
|
||||||
|
viewport.setZoom(4);
|
||||||
|
viewport.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await checkForScreenshot(page, page, screenShotPaths.mpr2.mprDisplayedCorrectlyZoomed);
|
||||||
|
});
|
||||||
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 301 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 296 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 308 KiB |
@ -17,19 +17,24 @@ const checkForScreenshot = async (
|
|||||||
delay = 100
|
delay = 100
|
||||||
) => {
|
) => {
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
for (let i = 1; i < attempts; i++) {
|
|
||||||
|
for (let i = 0; i < attempts; i++) {
|
||||||
try {
|
try {
|
||||||
await expect(locator).toHaveScreenshot(screenshotPath, {
|
await expect(locator).toHaveScreenshot(screenshotPath, {
|
||||||
maxDiffPixelRatio: 0.1,
|
maxDiffPixelRatio: 0.1,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (i === attempts) {
|
if (i === attempts - 1) {
|
||||||
throw new Error('Screenshot does not match.');
|
console.debug('Screenshot comparison failed after all attempts');
|
||||||
|
throw error; // Throw the original error with details instead of a generic message
|
||||||
}
|
}
|
||||||
await new Promise(resolve => setTimeout(resolve, delay));
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a fallback in case the loop exits unexpectedly
|
||||||
|
throw new Error('Screenshot comparison failed: loop exited without match or proper error');
|
||||||
};
|
};
|
||||||
|
|
||||||
export { checkForScreenshot };
|
export { checkForScreenshot };
|
||||||
|
|||||||
@ -26,6 +26,10 @@ const screenShotPaths = {
|
|||||||
mpr: {
|
mpr: {
|
||||||
mprDisplayedCorrectly: 'mprDisplayedCorrectly.png',
|
mprDisplayedCorrectly: 'mprDisplayedCorrectly.png',
|
||||||
},
|
},
|
||||||
|
mpr2: {
|
||||||
|
mprDisplayedCorrectly: 'mprDisplayedCorrectly.png',
|
||||||
|
mprDisplayedCorrectlyZoomed: 'mprDisplayedCorrectlyZoomed.png',
|
||||||
|
},
|
||||||
threeDFourUp: {
|
threeDFourUp: {
|
||||||
threeDFourUpDisplayedCorrectly: 'threeDFourUpDisplayedCorrectly.png',
|
threeDFourUpDisplayedCorrectly: 'threeDFourUpDisplayedCorrectly.png',
|
||||||
},
|
},
|
||||||
@ -75,11 +79,19 @@ const screenShotPaths = {
|
|||||||
segPreHydration: 'segPreHydration.png',
|
segPreHydration: 'segPreHydration.png',
|
||||||
segJumpToSegment: 'segJumpToSegment.png',
|
segJumpToSegment: 'segJumpToSegment.png',
|
||||||
},
|
},
|
||||||
|
segHydrationMPR: {
|
||||||
|
segPostHydration: 'segPostHydration.png',
|
||||||
|
segPostHydrationMPRAxialPrimary: 'segPostHydrationMPRAxialPrimary.png',
|
||||||
|
},
|
||||||
rtHydration: {
|
rtHydration: {
|
||||||
rtPostHydration: 'rtPostHydration.png',
|
rtPostHydration: 'rtPostHydration.png',
|
||||||
rtPreHydration: 'rtPreHydration.png',
|
rtPreHydration: 'rtPreHydration.png',
|
||||||
rtJumpToStructure: 'rtJumpToStructure.png',
|
rtJumpToStructure: 'rtJumpToStructure.png',
|
||||||
},
|
},
|
||||||
|
rtHydration2: {
|
||||||
|
rtPostHydration: 'rtPostHydration.png',
|
||||||
|
rtPreHydration: 'rtPreHydration.png',
|
||||||
|
},
|
||||||
crosshairs: {
|
crosshairs: {
|
||||||
crosshairsRendered: 'crosshairsRendered.png',
|
crosshairsRendered: 'crosshairsRendered.png',
|
||||||
crosshairsRotated: 'crosshairsRotated.png',
|
crosshairsRotated: 'crosshairsRotated.png',
|
||||||
|
|||||||