Skip to content

test(core): add unit tests for GenericViewport base class and planar/modality camera math#2789

Merged
sedghi merged 1 commit into
mainfrom
cornerstoneImprovedTesting
Jul 8, 2026
Merged

test(core): add unit tests for GenericViewport base class and planar/modality camera math#2789
sedghi merged 1 commit into
mainfrom
cornerstoneImprovedTesting

Conversation

@sedghi

@sedghi sedghi commented Jul 8, 2026

Copy link
Copy Markdown
Member

Context

The GenericViewport architecture in packages/core had large critical areas with little or no unit test coverage. This PR is a first round targeting the 5 most important undertested aspects, chosen from a per-file jest coverage audit (legacy Stack/Volume viewports and WebGL/VTK render paths intentionally excluded).

What is added

279 new jest tests across six files in packages/core/test/:

File Targets Tests
genericViewportBase.jest.js GenericViewport base class + genericViewportDisplaySetAccess 55
planarSliceBasis.jest.js planarSliceBasis slice geometry 37
planarViewReference.jest.js planarViewReference + PlanarViewReferenceController 67
planarRenderCamera.jest.js planarRenderCamera, setVtkCameraClippingRange, PlanarVolumeResolvedView 52
modalityViewportCameras.jest.js Video/ECG/Volume3D camera math 43
wsiTransformUtils.jest.js WSI world/index/canvas transforms 25

Highlights:

  • Base-class binding lifecycle including the subtle mid-await races: stale mount requests discarded, destroy during addData, remount supersede, source-role demotion of other bindings.
  • Slice basis geometry verified against hand-derived expected values for axial/sagittal/coronal, oblique 45 degree orientations, anisotropic spacing, and flipped/negative direction matrices.
  • View reference navigation depth: world focal point to slice resolution, opposite-normal slice index flip, plane-restriction-derived reorientation.
  • Camera resolve/derive round-trips: zoom, pan, rotation (including wrap and flip composition), display area SCALE/FIT modes, anchor-world invariants; volume resolved view now mirrors the existing stack parity suite.
  • All exports of the pure Video/ECG/Volume3D camera helpers and WSI transforms covered.

Coverage over src/RenderingEngine/GenericViewport

Overall: 33.7% -> 44.0% lines, 26.3% -> 38.1% branches.

Module Lines Branches
GenericViewport.ts 19.9 -> 88.7 5.0 -> 82.0
planarViewReference.ts 0 -> 97.1 0 -> 91.5
PlanarViewReferenceController.ts 30.7 -> 89.9 22.0 -> 80.9
planarRenderCamera.ts 37.9 -> 100 21.7 -> 86.7
planarSliceBasis.ts 67.2 -> 96.7 45.8 -> 89.8
videoViewportCamera.ts 59.5 -> 100 61.3 -> 100
wsiTransformUtils.ts 0 -> 100 0 -> 92.6
genericViewportDisplaySetAccess.ts 0 -> 100 0 -> 100

Findings surfaced while testing (no source changes in this PR)

  • wsiTransformUtils.ts: canvasToIndexForWSI multiplies the input canvas point by devicePixelRatio but indexToCanvasForWSI divides the entire transformed point (center translation included) by it — asymmetric DPR handling worth a look.
  • planarSliceBasis.ts (getGeometricImageVolumeCenter): the corner-averaging fallback loop is unreachable dead code because buildImageVolumeCorners returns [] whenever imageData is falsy.
  • planarViewReference.ts: for stack bindings a sliceIndex specifier override changes referencedImageId but not cameraFocalPoint (camera geometry comes from the single mounted image) — looks intentional, documented in a test.

Verification

Full packages/core jest suite: 30 suites, 514 passed, 2 skipped. No changes under packages/core/src or to existing tests.

Summary by CodeRabbit

  • Tests
    • Expanded automated coverage across viewport behavior, camera math, planar rendering, view references, and WSI transforms.
    • Added stronger validation for zoom, pan, rotation, clipping, slice selection, and coordinate conversion across image and volume workflows.
    • Improved coverage for edge cases like missing data, destroyed views, empty inputs, and zero-size canvases.

…modality camera math

Adds 279 jest tests across six new test files targeting the most
critical undertested areas of the GenericViewport architecture:

- genericViewportBase.jest.js: GenericViewport base class binding
  lifecycle, mount races (stale requests, destroy mid-load, source
  role demotion), presentation merge/fan-out, view state propagation,
  camera events, destroy semantics, and genericViewportDisplaySetAccess
- planarSliceBasis.jest.js: slice basis derivation for stack images
  and volumes across orientations, anisotropic spacing, rotated and
  flipped direction matrices, and image id index resolution
- planarViewReference.jest.js: planar view reference build/match APIs
  and PlanarViewReferenceController navigation (world point to slice,
  opposite-normal index flip, plane-restriction reorientation)
- planarRenderCamera.jest.js: resolvePlanarICamera and
  derivePlanarPresentation round-trips (zoom, pan, rotation, flips,
  display areas), renderer/actor application, clipping ranges, and
  PlanarVolumeResolvedView parity with the existing stack suite
- modalityViewportCameras.jest.js: Video/ECG/Volume3D viewport camera
  math (canvas mappings, scale modes, anchor/pan round-trips)
- wsiTransformUtils.jest.js: WSI world/index/canvas transforms and
  color transform branches

Coverage over src/RenderingEngine/GenericViewport rises from 33.7% to
44.0% lines and 26.3% to 38.1% branches, with the targeted modules at
89-100% lines.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds six new Jest test files under packages/core/test covering GenericViewport lifecycle and display-set access, video/ECG/volume3D camera math, planar render camera geometry, planar slice-basis computation, planar view reference/controller logic, and WSI transform utilities. No production code or exported entities are changed.

Changes

New Jest test suites

Layer / File(s) Summary
GenericViewport lifecycle and display-set access
packages/core/test/genericViewportBase.jest.js
Adds fixtures/harness and tests for setDisplaySets, addLoadedData lifecycle, removeData, presentation APIs, view state updates, destroy/dispose, mode/view-reference helpers, coordinate transforms, render status, resize, and genericViewportDisplaySetAccess normalization/guards.
Video, ECG, and volume3D camera math
packages/core/test/modalityViewportCameras.jest.js
Adds tests for default state creation, normalization, canvas mapping, and pan/anchor round-trips across three viewport camera modules.
Planar render camera geometry
packages/core/test/planarRenderCamera.jest.js
Adds tests for clipping range logic, anchor projection, presentation derivation, ICamera resolution, renderer/actor application, scale matrices, clipping planes, and PlanarVolumeResolvedView parity.
Planar slice basis computation
packages/core/test/planarSliceBasis.jest.js
Adds tests for image/volume slice basis derivation across orientations and fallback paths, plus related helper functions.
Planar view reference and controller
packages/core/test/planarViewReference.jest.js
Adds tests for view reference construction/viewability and PlanarViewReferenceController navigation across stack/volume bindings.
WSI transform utilities
packages/core/test/wsiTransformUtils.jest.js
Adds tests for WSI coordinate transforms, image data building, color transforms, and canvas/index conversions.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: wayfarer3130

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, semantic-release compliant, and matches the main change: adding tests for GenericViewport and related camera math.
Description check ✅ Passed Mostly complete: it covers context, changes, coverage, and testing, but the checklist and tested-environment template fields are not filled out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cornerstoneImprovedTesting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
packages/core/test/planarRenderCamera.jest.js (1)

21-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting shared geometry-assertion helpers.

expectPoint2Close/expectPoint3Close (and similarly-shaped helpers) are re-declared per spec file — the same expectPoint3Close reappears in planarSliceBasis.jest.js (lines 217-221). Centralizing these in a shared test-utils module would keep precision defaults consistent across the new planar spec files and ease future maintenance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/test/planarRenderCamera.jest.js` around lines 21 - 57, The
point-close assertion helpers are duplicated across planar spec files, including
expectPoint2Close and expectPoint3Close here and the same pattern in
planarSliceBasis.jest.js. Move these shared geometry assertions into a common
test-utils module and update the planar tests to import them so the precision
default stays consistent and future maintenance is centralized.
packages/core/test/planarViewReference.jest.js (1)

29-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fixture/mock setup duplicated with planarSliceBasis.jest.js.

createImage (lines 29-54) and the metaData.get mock configuration (lines 128-179) closely mirror the equivalent setup in packages/core/test/planarSliceBasis.jest.js (lines 27-54, 223-261). Extracting a shared fixtures/mock module for this test cohort would reduce drift as more planar spec files are added.

Also applies to: 128-179

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/test/planarViewReference.jest.js` around lines 29 - 54, The
test setup in createImage and the metaData.get mock is duplicated with the
planarSliceBasis spec, so factor this shared fixture/mock logic into a common
test helper used by both planarViewReference.jest.js and
planarSliceBasis.jest.js. Move the repeated image factory and metadata mock
configuration into a shared module, then import and reuse it from the existing
test files to keep the planar test cohort in sync and reduce drift.
packages/core/test/modalityViewportCameras.jest.js (1)

502-513: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract makeCanvas and metrics to the ecgViewportCamera describe scope.

The makeCanvas helper (line 502) and the metrics object (line 515) are defined inside the resolveECGCanvasMapping describe block but duplicated inline across the getPanForECGCanvasMapping and getAnchorWorldForCanvasPoint blocks (lines 598–606, 607–614, 623–631, 632–639, 664–672, 673–680, 696–704, 705–712). Moving both to the outer ecgViewportCamera describe scope would eliminate ~40 lines of repetition and keep the fixtures DRY.

♻️ Proposed refactor
 describe('ecgViewportCamera', () => {
   const timeRange = [0, 2000];
   const valueRange = [-1, 1];
+
+  function makeCanvas(clientWidth, clientHeight) {
+    const canvas = document.createElement('canvas');
+    Object.defineProperty(canvas, 'clientWidth', {
+      configurable: true,
+      value: clientWidth,
+    });
+    Object.defineProperty(canvas, 'clientHeight', {
+      configurable: true,
+      value: clientHeight,
+    });
+    return canvas;
+  }
+
+  const metrics = {
+    ecgWidth: 2000,
+    ecgHeight: 400,
+    channelScale: 10,
+    worldToCanvasRatio: 0.1,
+    xOffsetCanvas: 0,
+    yOffsetCanvas: 0,
+  };

   describe('createDefaultECGViewState', () => {

Then remove the inline makeCanvas and metrics definitions from each nested describe block.

Also applies to: 596-620, 622-660, 662-693, 695-742

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/test/modalityViewportCameras.jest.js` around lines 502 - 513,
The `makeCanvas` helper and `metrics` fixture are duplicated inside the nested
`resolveECGCanvasMapping` tests; move both definitions up to the outer
`ecgViewportCamera` describe scope so they can be reused by
`getPanForECGCanvasMapping` and `getAnchorWorldForCanvasPoint`. Then remove the
inline copies from each nested describe block and update the affected tests to
reference the shared `makeCanvas` and `metrics` symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/test/modalityViewportCameras.jest.js`:
- Around line 502-513: The `makeCanvas` helper and `metrics` fixture are
duplicated inside the nested `resolveECGCanvasMapping` tests; move both
definitions up to the outer `ecgViewportCamera` describe scope so they can be
reused by `getPanForECGCanvasMapping` and `getAnchorWorldForCanvasPoint`. Then
remove the inline copies from each nested describe block and update the affected
tests to reference the shared `makeCanvas` and `metrics` symbols.

In `@packages/core/test/planarRenderCamera.jest.js`:
- Around line 21-57: The point-close assertion helpers are duplicated across
planar spec files, including expectPoint2Close and expectPoint3Close here and
the same pattern in planarSliceBasis.jest.js. Move these shared geometry
assertions into a common test-utils module and update the planar tests to import
them so the precision default stays consistent and future maintenance is
centralized.

In `@packages/core/test/planarViewReference.jest.js`:
- Around line 29-54: The test setup in createImage and the metaData.get mock is
duplicated with the planarSliceBasis spec, so factor this shared fixture/mock
logic into a common test helper used by both planarViewReference.jest.js and
planarSliceBasis.jest.js. Move the repeated image factory and metadata mock
configuration into a shared module, then import and reuse it from the existing
test files to keep the planar test cohort in sync and reduce drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b2555d81-5cc3-41a8-9f3f-0a3788df05c9

📥 Commits

Reviewing files that changed from the base of the PR and between c60c191 and f6d1276.

📒 Files selected for processing (6)
  • packages/core/test/genericViewportBase.jest.js
  • packages/core/test/modalityViewportCameras.jest.js
  • packages/core/test/planarRenderCamera.jest.js
  • packages/core/test/planarSliceBasis.jest.js
  • packages/core/test/planarViewReference.jest.js
  • packages/core/test/wsiTransformUtils.jest.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant