test(e2e): update Playwright screenshots and add better tests and assertions for area calculations (#6022)
* Add (better) assertions for area calculation fixes. * Add SVG and measurement side panel area for freehand ROI test. * Add Playwright viewport screenshot scope migration guide. * Update cornerstonjs dependencies to 4.22.8. Ensure all versions of @babel/preset-env are 7.29.5. --------- Co-authored-by: Ghadeer Albattarni <165973963+GhadeerAlbattarni@users.noreply.github.com>
This commit is contained in:
1 parent
cb0cbe702c
commit
fe16e80cf3
174 files changed
+1071
-751
No files matched your search
@@ -54,7 +54,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.22.3",
|
||||
"@cornerstonejs/dicom-image-loader": "4.22.8",
|
||||
"@emotion/serialize": "1.3.3",
|
||||
"@ohif/core": "3.13.0-beta.74",
|
||||
"@ohif/extension-cornerstone": "3.13.0-beta.74",
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"@babel/plugin-transform-regenerator": "^7.16.7",
|
||||
"@babel/plugin-transform-runtime": "7.28.0",
|
||||
"@babel/plugin-transform-typescript": "^7.28.0",
|
||||
"@babel/preset-env": "7.28.0",
|
||||
"@babel/preset-env": "7.29.5",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@babel/plugin-transform-regenerator": "^7.16.7",
|
||||
"@babel/plugin-transform-runtime": "7.28.0",
|
||||
"@babel/plugin-transform-typescript": "^7.28.0",
|
||||
"@babel/preset-env": "7.28.0",
|
||||
"@babel/preset-env": "7.29.5",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"babel-loader": "^8.0.0-beta.4",
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/core": "4.22.3",
|
||||
"@cornerstonejs/dicom-image-loader": "4.22.3",
|
||||
"@cornerstonejs/core": "4.22.8",
|
||||
"@cornerstonejs/dicom-image-loader": "4.22.8",
|
||||
"@ohif/ui": "3.13.0-beta.74",
|
||||
"cornerstone-math": "0.1.10",
|
||||
"dicom-parser": "1.8.21"
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
sidebar_label: Playwright Viewport Screenshots
|
||||
title: Playwright viewport screenshot scope
|
||||
---
|
||||
|
||||
# Playwright viewport screenshot scope
|
||||
|
||||
Playwright tests whose assertions concern the viewport (rendering, overlays,
|
||||
measurements, segmentations, etc.) should scope `checkForScreenshot` to the
|
||||
viewport itself — or, at most, the entire viewport grid. They should **not**
|
||||
capture the surrounding OHIF UI (toolbar, side panels, study browser, header,
|
||||
etc.).
|
||||
|
||||
## Why
|
||||
|
||||
Full-page screenshots that include the app chrome are fragile and noisy:
|
||||
|
||||
- Any unrelated UI tweak (toolbar layout, panel styling, icon changes,
|
||||
measurement-panel rows, button states) regenerates the baseline and forces
|
||||
unrelated screenshot churn across the test suite.
|
||||
- Diffs become harder to read — a small viewport regression hides inside a
|
||||
screenshot dominated by chrome.
|
||||
- Tests that are nominally about viewport behavior end up gated on UI state
|
||||
that has nothing to do with the feature under test.
|
||||
|
||||
Scoping the screenshot to the viewport (or viewport grid) keeps baselines
|
||||
stable, makes failures meaningful, and aligns the screenshot with what the
|
||||
test is actually asserting.
|
||||
|
||||
## Migration
|
||||
|
||||
Pass a viewport-scoped locator as the second argument to `checkForScreenshot`:
|
||||
|
||||
- **Single viewport** — use `activeViewport.pane` (from
|
||||
`viewportPageObject.active`).
|
||||
- **Whole viewport grid** (e.g. MPR, 3D-four-up, or any test concerned with
|
||||
multi-viewport layout) — use `viewportPageObject.grid`.
|
||||
|
||||
Avoid passing `page` as the locator for viewport-focused tests — that captures
|
||||
the full page including all of the OHIF chrome.
|
||||
|
||||
**Before — full-page screenshot includes chrome:**
|
||||
|
||||
```ts
|
||||
await checkForScreenshot(
|
||||
page,
|
||||
page,
|
||||
screenShotPaths.length.lengthDisplayedCorrectly
|
||||
);
|
||||
```
|
||||
|
||||
**After — single viewport:**
|
||||
|
||||
```ts
|
||||
const activeViewport = await viewportPageObject.active;
|
||||
// ... interact with the viewport ...
|
||||
|
||||
await checkForScreenshot(
|
||||
page,
|
||||
activeViewport.pane,
|
||||
screenShotPaths.length.lengthDisplayedCorrectly
|
||||
);
|
||||
```
|
||||
|
||||
**After — viewport grid (multi-viewport layouts):**
|
||||
|
||||
```ts
|
||||
await mainToolbarPageObject.layoutSelection.MPR.click();
|
||||
|
||||
await checkForScreenshot(
|
||||
page,
|
||||
viewportPageObject.grid,
|
||||
screenShotPaths.mpr.mprDisplayedCorrectly
|
||||
);
|
||||
```
|
||||
|
||||
If a test legitimately needs to assert on chrome (panel content, toolbar
|
||||
state, measurement-row text), prefer a targeted locator for that element
|
||||
rather than a full-page screenshot that incidentally includes the viewport.
|
||||
@@ -182,6 +182,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
<div
|
||||
key={`empty-${indent}`}
|
||||
className="h-2"
|
||||
data-cy="data-row-detail-line-empty"
|
||||
></div>
|
||||
);
|
||||
}
|
||||
@@ -190,6 +191,7 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
<div
|
||||
key={cleanText}
|
||||
className="whitespace-pre-wrap"
|
||||
data-cy="data-row-detail-line"
|
||||
>
|
||||
{indentation}
|
||||
<span className="font-medium">{cleanText}</span>
|
||||
@@ -197,14 +199,17 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
);
|
||||
};
|
||||
|
||||
const renderDetails = (details: string[]) => {
|
||||
const renderDetails = (details: string[], variant: 'primary' | 'secondary') => {
|
||||
const visibleLines = details.slice(0, 4);
|
||||
const hiddenLines = details.slice(4);
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="cursor-help">
|
||||
<div
|
||||
className="cursor-help"
|
||||
data-cy={`data-row-details-${variant}`}
|
||||
>
|
||||
<div className="flex flex-col space-y-1">
|
||||
{visibleLines.map((line, lineIndex) =>
|
||||
renderDetailText(line, line.startsWith(' ') ? 1 : 0)
|
||||
@@ -418,12 +423,15 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
|
||||
|
||||
{/* Details Section */}
|
||||
{details && (details.primary?.length > 0 || details.secondary?.length > 0) && (
|
||||
<div className="ml-7 px-2 py-2">
|
||||
<div
|
||||
className="ml-7 px-2 py-2"
|
||||
data-cy="data-row-details"
|
||||
>
|
||||
<div className="text-secondary-foreground flex items-center gap-1 text-base leading-normal">
|
||||
{details.primary?.length > 0 && renderDetails(details.primary)}
|
||||
{details.primary?.length > 0 && renderDetails(details.primary, 'primary')}
|
||||
{details.secondary?.length > 0 && (
|
||||
<div className="text-muted-foreground ml-auto text-sm">
|
||||
{renderDetails(details.secondary)}
|
||||
{renderDetails(details.secondary, 'secondary')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -107,26 +107,28 @@ const Row = ({ item, index }: RowProps) => {
|
||||
|
||||
const { uid } = item;
|
||||
return (
|
||||
<DataRow
|
||||
key={item.uid}
|
||||
description={item.label}
|
||||
number={index + 1}
|
||||
title={item.label}
|
||||
colorHex={item.colorHex}
|
||||
isSelected={item.isSelected}
|
||||
details={item.displayText}
|
||||
onDelete={e => onAction(e, 'removeMeasurement', uid)}
|
||||
onSelect={e => onAction(e, 'jumpToMeasurement', uid)}
|
||||
onRename={e => onAction(e, 'renameMeasurement', uid)}
|
||||
onToggleVisibility={e => onAction(e, 'toggleVisibilityMeasurement', uid)}
|
||||
onToggleLocked={e => onAction(e, 'toggleLockMeasurement', uid)}
|
||||
onColor={e => onAction(e, 'changeMeasurementColor', uid)}
|
||||
disableEditing={disableEditing}
|
||||
isVisible={item.isVisible}
|
||||
isLocked={item.isLocked}
|
||||
>
|
||||
{item.isUnmapped && <DataRow.Status.Warning tooltip={item.statusTooltip} />}
|
||||
</DataRow>
|
||||
<div data-cy={`measurement-table-row-${index}`}>
|
||||
<DataRow
|
||||
key={item.uid}
|
||||
description={item.label}
|
||||
number={index + 1}
|
||||
title={item.label}
|
||||
colorHex={item.colorHex}
|
||||
isSelected={item.isSelected}
|
||||
details={item.displayText}
|
||||
onDelete={e => onAction(e, 'removeMeasurement', uid)}
|
||||
onSelect={e => onAction(e, 'jumpToMeasurement', uid)}
|
||||
onRename={e => onAction(e, 'renameMeasurement', uid)}
|
||||
onToggleVisibility={e => onAction(e, 'toggleVisibilityMeasurement', uid)}
|
||||
onToggleLocked={e => onAction(e, 'toggleLockMeasurement', uid)}
|
||||
onColor={e => onAction(e, 'changeMeasurementColor', uid)}
|
||||
disableEditing={disableEditing}
|
||||
isVisible={item.isVisible}
|
||||
isLocked={item.isLocked}
|
||||
>
|
||||
{item.isUnmapped && <DataRow.Status.Warning tooltip={item.statusTooltip} />}
|
||||
</DataRow>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user