diff --git a/.circleci/config.yml b/.circleci/config.yml index 5d70da547..8aecdcde3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -137,6 +137,7 @@ jobs: "GHSA-7r86-cg39-jmmj" # CVE-2026-27903 - minimatch same as above "GHSA-23c5-xmqv-rm74" # CVE-2026-27904 - minimatch same as above "GHSA-c2c7-rcm5-vvqj" # CVE-2026-33671 - picomatch is generally used for development and CI/CD purposes + "GHSA-mp2f-45pm-3cg9" # CVE-2026-53486 - decompress is called only ever decompresses a hash-pinned, hardcoded archive in a dev-only CLI ) # Build ignore flags diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index e8c6fe29d..8939fdfb8 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -9,8 +9,8 @@ concurrency: jobs: playwright-tests: - timeout-minutes: 60 - runs-on: self-hosted + timeout-minutes: 120 + runs-on: [self-hosted, nashua] strategy: fail-fast: false matrix: @@ -28,11 +28,16 @@ jobs: - name: Install dependencies run: bun install --frozen-lockfile - name: Install Playwright browsers - run: npx playwright install + # Only chromium is used (firefox/webkit projects are commented out in + # playwright.config.ts). Scoping the install to chromium avoids + # downloading + dep-validating browsers we never launch (the WebKit + # validation is what surfaces "missing libwoff1/libflite1/..." on hosts + # without those libs). + run: npx playwright install chromium - name: Run Playwright tests run: | export NODE_OPTIONS="--max_old_space_size=10192" - bun run test:e2e:coverage + bash .scripts/ci/with-nashua-lock.sh bun run test:e2e:coverage - name: Create directory of test results if: ${{ !cancelled() }} run: | diff --git a/.scripts/ci/with-nashua-lock.sh b/.scripts/ci/with-nashua-lock.sh new file mode 100644 index 000000000..620596f6c --- /dev/null +++ b/.scripts/ci/with-nashua-lock.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# +# Run a command while holding the nashua Playwright mutex. +# +# Only one Playwright run may execute at a time on the shared nashua self-hosted +# runner — across THIS repo (OHIF) AND the cornerstone3D repo. GitHub's +# `concurrency:` is scoped per-repository and cannot coordinate across the two +# orgs, so the mutex lives on the box's filesystem. +# +# This is OHIF's own copy (the script can't be shared across separate repos). It +# MUST use the SAME lock path as cornerstone3D's copy (NASHUA_LOCK_FILE default +# below) — if the paths differ, the mutex silently does nothing. +# +# flock holds the lock via fd 9 and it releases automatically when the wrapped +# command exits — including on job cancel / timeout / SIGKILL — so there are no +# stale locks to clean up. +# +# Usage: .scripts/ci/with-nashua-lock.sh [args...] + +# Strict mode: -e abort on any unhandled command failure, -u treat use of an +# unset variable as an error, -o pipefail make a pipeline fail if ANY stage +# fails (not just the last). Catches mistakes early instead of pressing on. +set -euo pipefail + +LOCK="${NASHUA_LOCK_FILE:-/var/tmp/nashua-playwright.lock}" # MUST match cornerstone3D's copy +LOCK_WAIT="${NASHUA_LOCK_WAIT:-5400}" # max seconds to wait + +# Guard: a command to wrap is required. With no arguments there is nothing to +# run, so print usage and exit instead of silently grabbing and releasing the +# lock for no reason (which would mask a mis-wired workflow step). +if [ "$#" -eq 0 ]; then + echo "usage: $0 [args...]" >&2 + exit 2 +fi + +# Open the lock file on fd 9 (read-write, create if missing, never truncate). +exec 9<>"$LOCK" || { echo "::error::cannot open lock file $LOCK"; exit 1; } + +# Try instantly; if busy, report who holds it, then block up to LOCK_WAIT. +if ! flock -n 9; then + echo "nashua Playwright runner busy — held by: $(cat "${LOCK}.info" 2>/dev/null || echo unknown). Waiting up to ${LOCK_WAIT}s…" + if ! flock -w "$LOCK_WAIT" 9; then + echo "::error::Timed out after ${LOCK_WAIT}s waiting for the nashua Playwright lock" + exit 1 + fi +fi + +# Record the current holder for other jobs' "held by" message (best-effort). +echo "${GITHUB_REPOSITORY:-local}#${GITHUB_RUN_ID:-0} @ $(date -u +%FT%TZ 2>/dev/null || true)" > "${LOCK}.info" 2>/dev/null || true +echo "✅ Acquired nashua Playwright lock ($LOCK); running: $*" + +# Replace the shell with the command. fd 9 is inherited (not close-on-exec), so +# the lock is held for the whole command and released when it exits. +exec "$@" diff --git a/bun.lock b/bun.lock index 5eb7c5e19..a24b2600d 100644 --- a/bun.lock +++ b/bun.lock @@ -50,7 +50,7 @@ }, "addOns/externals/devDependencies": { "name": "@externals/devDependencies", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@kitware/vtk.js": "34.15.1", @@ -131,14 +131,14 @@ }, "addOns/externals/dicom-microscopy-viewer": { "name": "@externals/dicom-microscopy-viewer", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "dicom-microscopy-viewer": "0.48.17", }, }, "extensions/cornerstone": { "name": "@ohif/extension-cornerstone", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.15.29", @@ -165,8 +165,8 @@ "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", "@cornerstonejs/dicom-image-loader": "4.15.29", - "@ohif/core": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/ui": "3.12.5", "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -178,7 +178,7 @@ }, "extensions/cornerstone-dicom-pmap": { "name": "@ohif/extension-cornerstone-dicom-pmap", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.15.29", @@ -186,10 +186,10 @@ "@kitware/vtk.js": "34.15.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/i18n": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/i18n": "3.12.5", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", @@ -200,15 +200,15 @@ }, "extensions/cornerstone-dicom-rt": { "name": "@ohif/extension-cornerstone-dicom-rt", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/i18n": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/i18n": "3.12.5", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", @@ -219,7 +219,7 @@ }, "extensions/cornerstone-dicom-seg": { "name": "@ohif/extension-cornerstone-dicom-seg", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.15.29", @@ -227,10 +227,10 @@ "@kitware/vtk.js": "34.15.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/i18n": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/i18n": "3.12.5", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", @@ -241,7 +241,7 @@ }, "extensions/cornerstone-dicom-sr": { "name": "@ohif/extension-cornerstone-dicom-sr", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.15.29", @@ -250,10 +250,10 @@ "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-measurement-tracking": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-measurement-tracking": "3.12.5", + "@ohif/ui": "3.12.5", "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -263,7 +263,7 @@ }, "extensions/cornerstone-dynamic-volume": { "name": "@ohif/extension-cornerstone-dynamic-volume", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/core": "4.15.29", @@ -271,11 +271,11 @@ "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/i18n": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/i18n": "3.12.5", + "@ohif/ui": "3.12.5", "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -285,7 +285,7 @@ }, "extensions/default": { "name": "@ohif/extension-default", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/calculate-suv": "1.1.0", @@ -294,8 +294,8 @@ "react-color": "2.19.3", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/i18n": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/i18n": "3.12.5", "dcmjs": "0.49.4", "dicomweb-client": "0.10.4", "prop-types": "15.8.1", @@ -309,7 +309,7 @@ }, "extensions/dicom-microscopy": { "name": "@ohif/extension-dicom-microscopy", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", @@ -320,10 +320,10 @@ "mathjs": "12.4.3", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/i18n": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/i18n": "3.12.5", + "@ohif/ui": "3.12.5", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", @@ -334,14 +334,14 @@ }, "extensions/dicom-pdf": { "name": "@ohif/extension-dicom-pdf", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/ui": "3.12.5", "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -351,14 +351,14 @@ }, "extensions/dicom-video": { "name": "@ohif/extension-dicom-video", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/ui": "3.12.5", "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -368,20 +368,20 @@ }, "extensions/measurement-tracking": { "name": "@ohif/extension-measurement-tracking", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", - "@ohif/ui": "3.12.4", + "@ohif/ui": "3.12.5", "@xstate/react": "3.2.2", "xstate": "4.38.3", }, "peerDependencies": { "@cornerstonejs/core": "4.15.29", "@cornerstonejs/tools": "4.15.29", - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/ui": "3.12.5", "classnames": "2.5.1", "dcmjs": "0.49.4", "lodash.debounce": "4.0.8", @@ -394,14 +394,14 @@ }, "extensions/test-extension": { "name": "@ohif/extension-test", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/ui": "3.12.5", "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -411,14 +411,14 @@ }, "extensions/tmtv": { "name": "@ohif/extension-tmtv", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/ui": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/ui": "3.12.5", "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -428,16 +428,16 @@ }, "extensions/usAnnotation": { "name": "@ohif/extension-ultrasound-pleura-bline", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/core": "4.15.29", "@cornerstonejs/tools": "4.15.29", - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/i18n": "3.12.4", - "@ohif/ui-next": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/i18n": "3.12.5", + "@ohif/ui-next": "3.12.5", }, "devDependencies": { "@babel/core": "7.28.0", @@ -476,7 +476,7 @@ }, "modes/basic": { "name": "@ohif/mode-basic", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", }, @@ -485,19 +485,19 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-rt": "3.12.4", - "@ohif/extension-cornerstone-dicom-seg": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-dicom-pdf": "3.12.4", - "@ohif/extension-dicom-video": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-rt": "3.12.5", + "@ohif/extension-cornerstone-dicom-seg": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-dicom-pdf": "3.12.5", + "@ohif/extension-dicom-video": "3.12.5", }, }, "modes/basic-dev-mode": { "name": "@ohif/mode-basic-dev-mode", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -507,17 +507,17 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-dicom-pdf": "3.12.4", - "@ohif/extension-dicom-video": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-dicom-pdf": "3.12.5", + "@ohif/extension-dicom-video": "3.12.5", }, }, "modes/basic-test-mode": { "name": "@ohif/mode-test", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -527,19 +527,19 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-dicom-pdf": "3.12.4", - "@ohif/extension-dicom-video": "3.12.4", - "@ohif/extension-measurement-tracking": "3.12.4", - "@ohif/extension-test": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-dicom-pdf": "3.12.5", + "@ohif/extension-dicom-video": "3.12.5", + "@ohif/extension-measurement-tracking": "3.12.5", + "@ohif/extension-test": "3.12.5", }, }, "modes/longitudinal": { "name": "@ohif/mode-longitudinal", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -549,33 +549,33 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-rt": "3.12.4", - "@ohif/extension-cornerstone-dicom-seg": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-dicom-pdf": "3.12.4", - "@ohif/extension-dicom-video": "3.12.4", - "@ohif/extension-measurement-tracking": "3.12.4", - "@ohif/mode-basic": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-rt": "3.12.5", + "@ohif/extension-cornerstone-dicom-seg": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-dicom-pdf": "3.12.5", + "@ohif/extension-dicom-video": "3.12.5", + "@ohif/extension-measurement-tracking": "3.12.5", + "@ohif/mode-basic": "3.12.5", }, }, "modes/microscopy": { "name": "@ohif/mode-microscopy", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-dicom-microscopy": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-dicom-microscopy": "3.12.5", }, }, "modes/preclinical-4d": { "name": "@ohif/mode-preclinical-4d", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", }, @@ -584,17 +584,17 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-seg": "3.12.4", - "@ohif/extension-cornerstone-dynamic-volume": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-tmtv": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-seg": "3.12.5", + "@ohif/extension-cornerstone-dynamic-volume": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-tmtv": "3.12.5", }, }, "modes/segmentation": { "name": "@ohif/mode-segmentation", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -623,20 +623,20 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-rt": "3.12.4", - "@ohif/extension-cornerstone-dicom-seg": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-dicom-pdf": "3.12.4", - "@ohif/extension-dicom-video": "3.12.4", - "@ohif/mode-basic": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-rt": "3.12.5", + "@ohif/extension-cornerstone-dicom-seg": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-dicom-pdf": "3.12.5", + "@ohif/extension-dicom-video": "3.12.5", + "@ohif/mode-basic": "3.12.5", }, }, "modes/tmtv": { "name": "@ohif/mode-tmtv", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -646,25 +646,25 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-dicom-pdf": "3.12.4", - "@ohif/extension-dicom-video": "3.12.4", - "@ohif/extension-measurement-tracking": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-dicom-pdf": "3.12.5", + "@ohif/extension-dicom-video": "3.12.5", + "@ohif/extension-measurement-tracking": "3.12.5", }, }, "modes/usAnnotation": { "name": "@ohif/mode-ultrasound-pleura-bline", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/core": "4.15.29", "@cornerstonejs/tools": "4.15.29", - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-ultrasound-pleura-bline": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-ultrasound-pleura-bline": "3.12.5", "i18next": "17.3.1", }, "devDependencies": { @@ -693,7 +693,7 @@ }, "platform/app": { "name": "@ohif/app", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", @@ -702,25 +702,25 @@ "@cornerstonejs/codec-openjph": "2.4.7", "@cornerstonejs/dicom-image-loader": "4.15.29", "@emotion/serialize": "1.3.3", - "@ohif/core": "3.12.4", - "@ohif/extension-cornerstone": "3.12.4", - "@ohif/extension-cornerstone-dicom-rt": "3.12.4", - "@ohif/extension-cornerstone-dicom-seg": "3.12.4", - "@ohif/extension-cornerstone-dicom-sr": "3.12.4", - "@ohif/extension-default": "3.12.4", - "@ohif/extension-dicom-microscopy": "3.12.4", - "@ohif/extension-dicom-pdf": "3.12.4", - "@ohif/extension-dicom-video": "3.12.4", - "@ohif/extension-test": "3.12.4", - "@ohif/extension-ultrasound-pleura-bline": "3.12.4", - "@ohif/i18n": "3.12.4", - "@ohif/mode-basic-dev-mode": "3.12.4", - "@ohif/mode-longitudinal": "3.12.4", - "@ohif/mode-microscopy": "3.12.4", - "@ohif/mode-test": "3.12.4", - "@ohif/mode-ultrasound-pleura-bline": "3.12.4", - "@ohif/ui": "3.12.4", - "@ohif/ui-next": "3.12.4", + "@ohif/core": "3.12.5", + "@ohif/extension-cornerstone": "3.12.5", + "@ohif/extension-cornerstone-dicom-rt": "3.12.5", + "@ohif/extension-cornerstone-dicom-seg": "3.12.5", + "@ohif/extension-cornerstone-dicom-sr": "3.12.5", + "@ohif/extension-default": "3.12.5", + "@ohif/extension-dicom-microscopy": "3.12.5", + "@ohif/extension-dicom-pdf": "3.12.5", + "@ohif/extension-dicom-video": "3.12.5", + "@ohif/extension-test": "3.12.5", + "@ohif/extension-ultrasound-pleura-bline": "3.12.5", + "@ohif/i18n": "3.12.5", + "@ohif/mode-basic-dev-mode": "3.12.5", + "@ohif/mode-longitudinal": "3.12.5", + "@ohif/mode-microscopy": "3.12.5", + "@ohif/mode-test": "3.12.5", + "@ohif/mode-ultrasound-pleura-bline": "3.12.5", + "@ohif/ui": "3.12.5", + "@ohif/ui-next": "3.12.5", "@svgr/webpack": "8.1.0", "@types/react": "18.3.23", "classnames": "2.5.1", @@ -772,7 +772,7 @@ }, "platform/cli": { "name": "@ohif/cli", - "version": "3.12.4", + "version": "3.12.5", "bin": { "ohif-cli": "src/index.js", }, @@ -796,7 +796,7 @@ }, "platform/core": { "name": "@ohif/core", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "dcmjs": "0.49.4", @@ -823,14 +823,14 @@ "@cornerstonejs/codec-openjph": "2.4.7", "@cornerstonejs/core": "4.15.29", "@cornerstonejs/dicom-image-loader": "4.15.29", - "@ohif/ui": "3.12.4", + "@ohif/ui": "3.12.5", "cornerstone-math": "0.1.10", "dicom-parser": "1.8.21", }, }, "platform/i18n": { "name": "@ohif/i18n", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@babel/runtime": "7.28.2", "i18next-locize-backend": "2.2.2", @@ -855,7 +855,7 @@ }, "platform/ui": { "name": "@ohif/ui", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@testing-library/react": "13.4.0", "browser-detect": "0.2.28", @@ -905,7 +905,7 @@ }, "platform/ui-next": { "name": "@ohif/ui-next", - "version": "3.12.4", + "version": "3.12.5", "dependencies": { "@radix-ui/react-accordion": "1.2.11", "@radix-ui/react-checkbox": "1.3.2", @@ -975,6 +975,7 @@ "rollup": "2.80.0", "serialize-javascript": "7.0.4", "shell-quote": "1.8.4", + "sigstore": "4.1.1", "svgo": "3.3.3", "tapable": "2.2.2", "tar": "7.5.11", @@ -1371,6 +1372,8 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + "@gar/promise-retry": ["@gar/promise-retry@1.0.3", "", {}, "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], @@ -1873,15 +1876,15 @@ "@sigstore/bundle": ["@sigstore/bundle@4.0.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A=="], - "@sigstore/core": ["@sigstore/core@3.1.0", "", {}, "sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A=="], + "@sigstore/core": ["@sigstore/core@3.2.1", "", {}, "sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g=="], "@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.5.0", "", {}, "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA=="], - "@sigstore/sign": ["@sigstore/sign@4.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.3", "proc-log": "^6.1.0", "promise-retry": "^2.0.1" } }, "sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg=="], + "@sigstore/sign": ["@sigstore/sign@4.1.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.2", "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.4", "proc-log": "^6.1.0" } }, "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ=="], - "@sigstore/tuf": ["@sigstore/tuf@4.0.1", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.1.0" } }, "sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw=="], + "@sigstore/tuf": ["@sigstore/tuf@4.0.2", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.1.0" } }, "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ=="], - "@sigstore/verify": ["@sigstore/verify@3.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag=="], + "@sigstore/verify": ["@sigstore/verify@3.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA=="], "@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], @@ -4725,7 +4728,7 @@ "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "sigstore": ["sigstore@4.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.0", "@sigstore/tuf": "^4.0.1", "@sigstore/verify": "^3.1.0" } }, "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA=="], + "sigstore": ["sigstore@4.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.1", "@sigstore/tuf": "^4.0.2", "@sigstore/verify": "^3.1.1" } }, "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w=="], "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], @@ -5589,7 +5592,7 @@ "@rspack/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.1", "", { "dependencies": { "@emnapi/core": "^1.4.5", "@emnapi/runtime": "^1.4.5", "@tybys/wasm-util": "^0.10.0" } }, "sha512-KVlQ/jgywZpixGCKMNwxStmmbYEMyokZpCf2YuIChhfJA2uqfAKNEM8INz7zzTo55iEXfBhIIs3VqYyqzDLj8g=="], - "@sigstore/sign/make-fetch-happen": ["make-fetch-happen@15.0.3", "", { "dependencies": { "@npmcli/agent": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "ssri": "^13.0.0" } }, "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw=="], + "@sigstore/sign/make-fetch-happen": ["make-fetch-happen@15.0.6", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" } }, "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw=="], "@sigstore/sign/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], @@ -6665,6 +6668,8 @@ "@rspack/binding-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], + "@sigstore/sign/make-fetch-happen/@npmcli/redact": ["@npmcli/redact@4.0.0", "", {}, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="], + "@sigstore/sign/make-fetch-happen/minipass-fetch": ["minipass-fetch@5.0.1", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^2.0.0", "minizlib": "^3.0.1" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-yHK8pb0iCGat0lDrs/D6RZmCdaBT64tULXjdxjSMAqoDi18Q3qKEUTHypHQZQd9+FYpIS+lkvpq6C/R6SbUeRw=="], "@sigstore/sign/make-fetch-happen/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], diff --git a/package.json b/package.json index 8b5e00b83..83cdd2fdc 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "install:update-lockfile": "yarn install && bun install --config=./bunfig.update-lockfile.toml", "install:yarn:frozen-lockfile": "echo Deprecated - use install:frozen && yarn install --frozen-lockfile", "install:frozen": "yarn install --frozen-lockfile", - "audit": "bun audit --ignore=GHSA-3ppc-4f35-3m26 --ignore=GHSA-7r86-cg39-jmmj --ignore=GHSA-23c5-xmqv-rm74 --ignore=GHSA-c2c7-rcm5-vvqj", + "audit": "bun audit --ignore=GHSA-3ppc-4f35-3m26 --ignore=GHSA-7r86-cg39-jmmj --ignore=GHSA-23c5-xmqv-rm74 --ignore=GHSA-c2c7-rcm5-vvqj --ignore=GHSA-mp2f-45pm-3cg9", "preinstall": "node preinstall.js", "start": "yarn run dev", "test": "yarn run test:unit", @@ -167,7 +167,8 @@ "tmp": "0.2.7", "form-data": "4.0.6", "ws": "8.21.0", - "shell-quote": "1.8.4" + "shell-quote": "1.8.4", + "sigstore": "4.1.1" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/playwright.config.ts b/playwright.config.ts index 1c7307842..c686adb49 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,9 +2,11 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', + globalSetup: './tests/globalSetup.ts', fullyParallel: !!process.env.CI, forbidOnly: !!process.env.CI, - retries: process.env.CI ? 3 : 0, + retries: process.env.CI ? 1 : 0, + maxFailures: process.env.CI ? 10 : undefined, workers: process.env.CI ? 6 : undefined, snapshotPathTemplate: './tests/screenshots{/projectName}/{testFilePath}/{arg}{ext}', outputDir: './tests/test-results', @@ -20,6 +22,10 @@ export default defineConfig({ launchOptions: { // do not hide the scrollbars so that we can assert their look-and-feel ignoreDefaultArgs: ['--hide-scrollbars'], + // Use the runner's EGL GPU stack for WebGL. Without this, Chromium falls + // back to software GL on the self-hosted (nashua) runner and cornerstone + // rendering produces wrong/blank frames, failing every screenshot test. + args: ['--use-gl=egl'], }, }, @@ -42,7 +48,8 @@ export default defineConfig({ //}, ], webServer: { - command: 'cross-env APP_CONFIG=config/e2e.js COVERAGE=true OHIF_PORT=3335 nyc yarn start', + command: + 'cross-env APP_CONFIG=config/e2e.js COVERAGE=true OHIF_PORT=3335 OHIF_OPEN=false nyc yarn start', url: 'http://localhost:3335', reuseExistingServer: !process.env.CI, timeout: 360_000, diff --git a/tests/ArrowAnnotate.spec.ts b/tests/ArrowAnnotate.spec.ts index f329d0e22..fc62cbc51 100644 --- a/tests/ArrowAnnotate.spec.ts +++ b/tests/ArrowAnnotate.spec.ts @@ -32,7 +32,7 @@ test('should display the arrow tool and allow free-form text to be entered', asy await checkForScreenshot({ page, - maxDiffPixelRatio: 0.0075, + locator: page.getByTestId('viewport-grid'), screenshotPath: screenShotPaths.arrowAnnotate.arrowAnnotateDisplayedCorrectly0, }); @@ -46,7 +46,7 @@ test('should display the arrow tool and allow free-form text to be entered', asy await checkForScreenshot({ page, - maxDiffPixelRatio: 0.0075, + locator: page.getByTestId('viewport-grid'), screenshotPath: screenShotPaths.arrowAnnotate.arrowAnnotateDisplayedCorrectly1, }); @@ -60,7 +60,7 @@ test('should display the arrow tool and allow free-form text to be entered', asy await checkForScreenshot({ page, - maxDiffPixelRatio: 0.0075, + locator: page.getByTestId('viewport-grid'), screenshotPath: screenShotPaths.arrowAnnotate.arrowAnnotateDisplayedCorrectly2, }); }); diff --git a/tests/MPRThenSEGOverlayNoHydration.spec.ts b/tests/MPRThenSEGOverlayNoHydration.spec.ts index b67e3d8d9..9960bd602 100644 --- a/tests/MPRThenSEGOverlayNoHydration.spec.ts +++ b/tests/MPRThenSEGOverlayNoHydration.spec.ts @@ -19,11 +19,11 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu', await page.waitForTimeout(5000); - await checkForScreenshot( + await checkForScreenshot({ page, - page, - screenShotPaths.mprThenSEGOverlayNoHydration.mprPreSEGOverlayNoHydration - ); + locator: page.getByTestId('viewport-grid'), + screenshotPath: screenShotPaths.mprThenSEGOverlayNoHydration.mprPreSEGOverlayNoHydration, + }); // Hover over the middle/sagittal viewport so that the data overlay menu is available. await viewportPageObject.getById('mpr-axial').pane.hover(); @@ -39,9 +39,9 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu', await page.waitForTimeout(5000); - await checkForScreenshot( + await checkForScreenshot({ page, - page, - screenShotPaths.mprThenSEGOverlayNoHydration.mprPostSEGOverlayNoHydration - ); + locator: page.getByTestId('viewport-grid'), + screenshotPath: screenShotPaths.mprThenSEGOverlayNoHydration.mprPostSEGOverlayNoHydration, + }); }); diff --git a/tests/RTHydration.spec.ts b/tests/RTHydration.spec.ts index 25a67183a..5747f50f5 100644 --- a/tests/RTHydration.spec.ts +++ b/tests/RTHydration.spec.ts @@ -15,13 +15,25 @@ test('should hydrate RT reports correctly', async ({ await rightPanelPageObject.toggle(); await leftPanelPageObject.loadSeriesByModality('RTSTRUCT'); await page.waitForTimeout(5000); - await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtPreHydration); + await checkForScreenshot({ + page, + locator: page.getByTestId('viewport-grid'), + screenshotPath: screenShotPaths.rtHydration.rtPreHydration, + }); await DOMOverlayPageObject.viewport.segmentationHydration.yes.click(); await page.waitForTimeout(5000); - await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtPostHydration); + await checkForScreenshot({ + page, + locator: page.getByTestId('viewport-grid'), + screenshotPath: screenShotPaths.rtHydration.rtPostHydration, + }); await rightPanelPageObject.labelMapSegmentationPanel.panel.segmentByText('Small Sphere').click(); await page.waitForTimeout(5000); - await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtJumpToStructure); + await checkForScreenshot({ + page, + locator: page.getByTestId('viewport-grid'), + screenshotPath: screenShotPaths.rtHydration.rtJumpToStructure, + }); }); diff --git a/tests/RTHydration2.spec.ts b/tests/RTHydration2.spec.ts index 431f054bc..fcbcfa9af 100644 --- a/tests/RTHydration2.spec.ts +++ b/tests/RTHydration2.spec.ts @@ -15,7 +15,11 @@ test('should hydrate RT reports correctly', async ({ await rightPanelPageObject.toggle(); await leftPanelPageObject.loadSeriesByModality('RTSTRUCT'); - await checkForScreenshot(page, page, screenShotPaths.rtHydration2.rtPreHydration); + await checkForScreenshot({ + page, + locator: page.getByTestId('viewport-grid'), + screenshotPath: screenShotPaths.rtHydration2.rtPreHydration, + }); // wait for 3 seconds await page.evaluate(() => { // Access cornerstone directly from the window object @@ -40,5 +44,9 @@ test('should hydrate RT reports correctly', async ({ // should preserve zoom and pan and scroll position after hydration await DOMOverlayPageObject.viewport.segmentationHydration.yes.click(); - await checkForScreenshot(page, page, screenShotPaths.rtHydration.rtPostHydration); + await checkForScreenshot({ + page, + locator: page.getByTestId('viewport-grid'), + screenshotPath: screenShotPaths.rtHydration2.rtPostHydration, + }); }); diff --git a/tests/SEGDataOverlayNoHydrationThenMPR.spec.ts b/tests/SEGDataOverlayNoHydrationThenMPR.spec.ts index 93dcbf1fd..6a020e2f0 100644 --- a/tests/SEGDataOverlayNoHydrationThenMPR.spec.ts +++ b/tests/SEGDataOverlayNoHydrationThenMPR.spec.ts @@ -27,21 +27,23 @@ test('should launch MPR with unhydrated SEG chosen from the data overlay menu', await page.waitForTimeout(5000); - await checkForScreenshot( + await checkForScreenshot({ page, - page, - screenShotPaths.segDataOverlayNoHydrationThenMPR.segDataOverlayNoHydrationPreMPR - ); + locator: page.getByTestId('viewport-grid'), + screenshotPath: + screenShotPaths.segDataOverlayNoHydrationThenMPR.segDataOverlayNoHydrationPreMPR, + }); await mainToolbarPageObject.layoutSelection.MPR.click(); await page.waitForTimeout(5000); - await checkForScreenshot( + await checkForScreenshot({ page, - page, - screenShotPaths.segDataOverlayNoHydrationThenMPR.segDataOverlayNoHydrationPostMPR - ); + locator: page.getByTestId('viewport-grid'), + screenshotPath: + screenShotPaths.segDataOverlayNoHydrationThenMPR.segDataOverlayNoHydrationPostMPR, + }); // Adding an overlay should not show the LOAD button. assertNumberOfModalityLoadBadges({ page, expectedCount: 0 }); diff --git a/tests/Worklist.spec.ts b/tests/Worklist.spec.ts index 8c01e1683..c1e86487e 100644 --- a/tests/Worklist.spec.ts +++ b/tests/Worklist.spec.ts @@ -35,10 +35,12 @@ test('should render scroll bars with the correct look-and-feel', async ({ page } // Additional wait to ensure scrollbar rendering is stable await page.waitForTimeout(1000); + expandedStudyRow.scrollIntoViewIfNeeded(); + await checkForScreenshot({ page, locator: expandedStudyRow, - normalizedClip: { x: 0.97, y: 0.35, width: 0.03, height: 0.65 }, + normalizedClip: { x: 0.97, y: 0.35, width: 0.028, height: 0.65 }, screenshotPath: screenShotPaths.workList.scrollBarRenderedProperly, }); }); diff --git a/tests/globalSetup.ts b/tests/globalSetup.ts new file mode 100644 index 000000000..9b4537f9c --- /dev/null +++ b/tests/globalSetup.ts @@ -0,0 +1,36 @@ +/** + * Playwright global setup (wired via `globalSetup` in playwright.config.ts). + * + * Before the test suite runs, open the viewer once against a known study to + * "warm up" the dev server: trigger the first (slow) lazy webpack compilation, + * prime the browser/codec caches, and fetch the study metadata. Without this, + * the first real spec frequently times out waiting on a cold server compile. + * This run only loads the page and waits for a viewport pane to render; it makes + * no assertions. + */ +import { chromium, type FullConfig } from '@playwright/test'; + +const warmupStudyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785'; +const warmupTimeout = 180_000; + +export default async function globalSetup(config: FullConfig) { + const baseURL = String(config.projects[0]?.use?.baseURL || 'http://localhost:3335'); + const warmupURL = new URL( + `/viewer/ohif?StudyInstanceUIDs=${warmupStudyInstanceUID}`, + baseURL + ).toString(); + const browser = await chromium.launch(); + const page = await browser.newPage(); + + try { + page.setDefaultTimeout(warmupTimeout); + await page.goto(warmupURL, { waitUntil: 'domcontentloaded', timeout: warmupTimeout }); + await page.waitForLoadState('networkidle', { timeout: warmupTimeout }).catch(() => {}); + await page + .locator('[data-cy="viewport-pane"]') + .first() + .waitFor({ state: 'visible', timeout: warmupTimeout }); + } finally { + await browser.close(); + } +} diff --git a/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly0.png b/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly0.png index a7e8943ec..3868dd707 100644 Binary files a/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly0.png and b/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly0.png differ diff --git a/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly1.png b/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly1.png index f499b3429..74dd9c483 100644 Binary files a/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly1.png and b/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly1.png differ diff --git a/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly2.png b/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly2.png index 51ddfecad..f94d25448 100644 Binary files a/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly2.png and b/tests/screenshots/chromium/ArrowAnnotate.spec.ts/arrowAnnotateDisplayedCorrectly2.png differ diff --git a/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPostSEGOverlayNoHydration.png b/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPostSEGOverlayNoHydration.png index 652e7a541..70484f9a1 100644 Binary files a/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPostSEGOverlayNoHydration.png and b/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPostSEGOverlayNoHydration.png differ diff --git a/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPreSEGOverlayNoHydration.png b/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPreSEGOverlayNoHydration.png index 1710c1e2d..597f255ec 100644 Binary files a/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPreSEGOverlayNoHydration.png and b/tests/screenshots/chromium/MPRThenSEGOverlayNoHydration.spec.ts/mprPreSEGOverlayNoHydration.png differ diff --git a/tests/screenshots/chromium/RTHydration.spec.ts/rtJumpToStructure.png b/tests/screenshots/chromium/RTHydration.spec.ts/rtJumpToStructure.png index afc09d1f5..4143e579d 100644 Binary files a/tests/screenshots/chromium/RTHydration.spec.ts/rtJumpToStructure.png and b/tests/screenshots/chromium/RTHydration.spec.ts/rtJumpToStructure.png differ diff --git a/tests/screenshots/chromium/RTHydration.spec.ts/rtPostHydration.png b/tests/screenshots/chromium/RTHydration.spec.ts/rtPostHydration.png index 3c068854d..bfff04ea2 100644 Binary files a/tests/screenshots/chromium/RTHydration.spec.ts/rtPostHydration.png and b/tests/screenshots/chromium/RTHydration.spec.ts/rtPostHydration.png differ diff --git a/tests/screenshots/chromium/RTHydration.spec.ts/rtPreHydration.png b/tests/screenshots/chromium/RTHydration.spec.ts/rtPreHydration.png index 02a8f103d..86a26ae25 100644 Binary files a/tests/screenshots/chromium/RTHydration.spec.ts/rtPreHydration.png and b/tests/screenshots/chromium/RTHydration.spec.ts/rtPreHydration.png differ diff --git a/tests/screenshots/chromium/RTHydration2.spec.ts/rtPostHydration.png b/tests/screenshots/chromium/RTHydration2.spec.ts/rtPostHydration.png index d0c12f0d3..a36e7e76d 100644 Binary files a/tests/screenshots/chromium/RTHydration2.spec.ts/rtPostHydration.png and b/tests/screenshots/chromium/RTHydration2.spec.ts/rtPostHydration.png differ diff --git a/tests/screenshots/chromium/RTHydration2.spec.ts/rtPreHydration.png b/tests/screenshots/chromium/RTHydration2.spec.ts/rtPreHydration.png index 048000368..ed811f3c7 100644 Binary files a/tests/screenshots/chromium/RTHydration2.spec.ts/rtPreHydration.png and b/tests/screenshots/chromium/RTHydration2.spec.ts/rtPreHydration.png differ diff --git a/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPostMpr.png b/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPostMpr.png index 18f8ffed0..450d65d0d 100644 Binary files a/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPostMpr.png and b/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPostMpr.png differ diff --git a/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPreMpr.png b/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPreMpr.png index aee462ed7..264d9e954 100644 Binary files a/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPreMpr.png and b/tests/screenshots/chromium/SEGDataOverlayNoHydrationThenMPR.spec.ts/segDataOverlayNoHydrationPreMpr.png differ diff --git a/tests/screenshots/chromium/Worklist.spec.ts/scrollBarRenderedProperly.png b/tests/screenshots/chromium/Worklist.spec.ts/scrollBarRenderedProperly.png index fd98dce2c..5c8adc4a0 100644 Binary files a/tests/screenshots/chromium/Worklist.spec.ts/scrollBarRenderedProperly.png and b/tests/screenshots/chromium/Worklist.spec.ts/scrollBarRenderedProperly.png differ diff --git a/version.txt b/version.txt index f46612b4c..35f236d6e 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -3.12.5 \ No newline at end of file +3.12.6 diff --git a/yarn.lock b/yarn.lock index 7ebec9789..4a5f17b66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2407,6 +2407,11 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429" integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg== +"@gar/promise-retry@^1.0.0", "@gar/promise-retry@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@gar/promise-retry/-/promise-retry-1.0.3.tgz#65e726428e794bc4453948e0a41e6de4215ce8b0" + integrity sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA== + "@humanfs/core@^0.19.1": version "0.19.1" resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" @@ -4504,43 +4509,43 @@ dependencies: "@sigstore/protobuf-specs" "^0.5.0" -"@sigstore/core@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@sigstore/core/-/core-3.1.0.tgz#b418de73f56333ad9e369b915173d8c98e9b96d5" - integrity sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A== +"@sigstore/core@^3.2.0", "@sigstore/core@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@sigstore/core/-/core-3.2.1.tgz#4efd4ab0f59e768b6daf65612024ba925787de72" + integrity sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g== "@sigstore/protobuf-specs@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz#e5f029edcb3a4329853a09b603011e61043eb005" integrity sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA== -"@sigstore/sign@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@sigstore/sign/-/sign-4.1.0.tgz#63df15a137337b29f463a1d1c51e1f7d4c1db2f1" - integrity sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg== +"@sigstore/sign@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@sigstore/sign/-/sign-4.1.1.tgz#34765fe4a190d693340c0771a3d150a397bcfc55" + integrity sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ== dependencies: + "@gar/promise-retry" "^1.0.2" "@sigstore/bundle" "^4.0.0" - "@sigstore/core" "^3.1.0" + "@sigstore/core" "^3.2.0" "@sigstore/protobuf-specs" "^0.5.0" - make-fetch-happen "^15.0.3" + make-fetch-happen "^15.0.4" proc-log "^6.1.0" - promise-retry "^2.0.1" -"@sigstore/tuf@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-4.0.1.tgz#9b080390936d79ea3b6a893b64baf3123e92d6d3" - integrity sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw== +"@sigstore/tuf@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-4.0.2.tgz#7d2fa2abcd5afa5baf752671d14a1c6ed0ed3196" + integrity sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ== dependencies: "@sigstore/protobuf-specs" "^0.5.0" tuf-js "^4.1.0" -"@sigstore/verify@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@sigstore/verify/-/verify-3.1.0.tgz#4046d4186421db779501fe87fa5acaa5d4d21b08" - integrity sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag== +"@sigstore/verify@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@sigstore/verify/-/verify-3.1.1.tgz#13c1c1cff28fe81f662de29d85ba5ae048fcbd8d" + integrity sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA== dependencies: "@sigstore/bundle" "^4.0.0" - "@sigstore/core" "^3.1.0" + "@sigstore/core" "^3.2.1" "@sigstore/protobuf-specs" "^0.5.0" "@sinclair/typebox@^0.27.8": @@ -13526,7 +13531,7 @@ make-fetch-happen@15.0.2: promise-retry "^2.0.1" ssri "^12.0.0" -make-fetch-happen@^15.0.0, make-fetch-happen@^15.0.1, make-fetch-happen@^15.0.3: +make-fetch-happen@^15.0.0, make-fetch-happen@^15.0.1: version "15.0.3" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz#1578d72885f2b3f9e5daa120b36a14fc31a84610" integrity sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw== @@ -13543,6 +13548,24 @@ make-fetch-happen@^15.0.0, make-fetch-happen@^15.0.1, make-fetch-happen@^15.0.3: promise-retry "^2.0.1" ssri "^13.0.0" +make-fetch-happen@^15.0.4: + version "15.0.6" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz#079dee708c88a04a1f79735bb02789a63597840e" + integrity sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw== + dependencies: + "@gar/promise-retry" "^1.0.0" + "@npmcli/agent" "^4.0.0" + "@npmcli/redact" "^4.0.0" + cacache "^20.0.1" + http-cache-semantics "^4.1.1" + minipass "^7.0.2" + minipass-fetch "^5.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^1.0.0" + proc-log "^6.0.0" + ssri "^13.0.0" + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -17869,17 +17892,17 @@ signal-exit@^4.0.1, signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -sigstore@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-4.1.0.tgz#d34b92a544a05e003a2430209d26d8dfafd805a0" - integrity sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA== +sigstore@4.1.1, sigstore@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-4.1.1.tgz#2959993dbf978c759a5d23d854cbd3729b6dcd97" + integrity sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w== dependencies: "@sigstore/bundle" "^4.0.0" - "@sigstore/core" "^3.1.0" + "@sigstore/core" "^3.2.1" "@sigstore/protobuf-specs" "^0.5.0" - "@sigstore/sign" "^4.1.0" - "@sigstore/tuf" "^4.0.1" - "@sigstore/verify" "^3.1.0" + "@sigstore/sign" "^4.1.1" + "@sigstore/tuf" "^4.0.2" + "@sigstore/verify" "^3.1.1" sirv@^3.0.2: version "3.0.2"