* 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>
121 lines
4.7 KiB
TypeScript
121 lines
4.7 KiB
TypeScript
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);
|
||
});
|