ohif-viewer/tests/FreehandROI.spec.ts
Alireza 6dd150d401
fix: ohif tests to run with cornerstone 3d 5.0 (#6043)
* chore(tests): Update multiple screenshot test images for various specs

* feat(screenshot-reviewer): Add screenshot review tool and update package.json scripts

* fix(DICOMSRDisplayTool): Improve actor presence check in viewport

* chore(tests): Update multiple screenshot assets for various specs

* chore(tests): Integrate waitForPaintToSettle and waitForViewportsRendered in multiple specs for improved rendering stability

* chore(tests): Update screenshot assets for SEGHydration and SEGNoHydration specs

* test: update progressive loading screenshots

* jest 30 test fixes for compatibility with pnpm cs3d

* Use correct setDisplaySets instead of setDataId

* fix: Naming change for LegacyVolumeViewport3D

* Update to allow tolerance for contour tests

* update

* fix

* refactor: Replace instanceof checks with utility functions for viewport type validation

* fix: Update createSegmentationForViewport to handle undefined displaySetInstanceUID gracefully

* bun lock

* fix: Install cs3d with pnpm instead of bun

* Update node version for playwright

* Update to v5.0.0 of cs3d

* fix: Build dependency

* audit

* Change to a web await retry assert

* Fix timing related test failures

* fix: Freehand close

---------

Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2026-06-09 20:25:14 -04:00

121 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should not fire MEASUREMENT_ADDED when clicking the annotation text', async ({
page,
DOMOverlayPageObject,
mainToolbarPageObject,
viewportPageObject,
}) => {
await mainToolbarPageObject.measurementTools.freehandROI.click();
const activeViewport = await viewportPageObject.active;
await activeViewport.normalizedDragAt({
start: { x: 0.35, y: 0.35 },
end: { x: 0.6, y: 0.55 },
config: { steps: 20, delay: 30 },
});
await DOMOverlayPageObject.viewport.measurementTracking.confirm.click();
const measurementAdded = await subscribeToMeasurementAdded(page);
try {
const annotation = activeViewport.nthAnnotation(0);
await annotation.text.click();
await expect(measurementAdded.waitFired(1000)).rejects.toThrow();
} finally {
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.
//
// The closing gap must be a fixed number of canvas pixels, not a normalized
// fraction: cs3d's close-proximity threshold is in pixels, so a fractional gap
// (0.01 × height) grew past the threshold on taller CI viewports and the
// contour never auto-closed (intermittent "no cachedStats"). Derive it from the
// viewport bbox so the gap is viewport-size-independent.
const CLOSE_GAP_PX = 6;
const viewportBox = await activeViewport.pane.boundingBox();
if (!viewportBox) {
throw new Error('Active viewport bounding box not found');
}
const closeY = 0.3 + CLOSE_GAP_PX / viewportBox.height;
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: closeY },
];
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);
});