ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OHIF Viewer 端到端测试指南:基于 Playwright 的用例编写、截图验证与运行调试

OHIF Viewer 端到端测试指南:基于 Playwright 的用例编写、截图验证与运行调试 OHIF Viewer 端到端测试指南基于 Playwright 的用例编写、截图验证与运行调试【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers本指南以 OHIF Viewer 官方 3.11 版 Playwright 测试文档为主体结合当前仓库monorepo 根目录的playwright.config.ts、tests/目录下的工具源码展开系统讲解如何在 OHIF Viewer 上编写端到端测试从选定指定研究实例与 Mode、通过checkForScreenshot做像素级视觉回归到模拟鼠标拖拽、通过page.evaluate直接驱动 Cornerstone3D 与各 Service以及如何使用test:e2e:ui、test:e2e:ci、test:e2e:headed等脚本和 Playwright VSCode 扩展录制测试。读完本文你将能够独立为 OHIF 的任何工具、扩展或模式编写稳定、可复现、可纳入 CI 的端到端测试。OHIF 的 Playwright 测试基础设施概览OHIF Viewer 使用 Playwright 统一编排testDir: ./tests所有*.spec.ts测试文件都放在该目录下如tests/ArrowAnnotate.spec.ts、tests/SEGHydration.spec.ts、tests/MPR.spec.ts等globalSetup: ./tests/globalSetup.ts套件运行前先执行一次预热脚本snapshotPathTemplate: ./tests/screenshots{/projectName}/{testFilePath}/{arg}{ext}截图基准golden/snapshot统一落在tests/screenshots/chromium/spec 名/截图名.pngoutputDir: ./tests/test-results失败现场trace、video 等输出目录reporter: [[html, { outputFolder: ./tests/playwright-report }]]生成 HTML 报告可用pnpm exec playwright show-report tests/playwright-report打开use.baseURL: http://localhost:3335所有测试默认访问 3335 端口testIdAttribute: data-cyOHIF 组件统一以data-cy作为测试定位属性例如globalSetup.ts中等待[data-cyLayout]出现webServer每次运行前自动以config/e2e.jsCOVERAGEtrueOHIF_PORT3335启动应用并开启nyc覆盖率采集本地开发时reuseExistingServer为真可复用已启动的 3335 服务。需要说明的是虽然 Playwright 官方支持多浏览器项目当前配置中firefox与webkit项目被注释保留注释中分别说明了 Firefox 测试待修复、WebKit 需等SharedArrayBuffer支持当前实际执行的是chromium项目。环境准备与常用运行命令首次运行前的依赖安装如果尚未安装过 Playwright 浏览器需要先执行一次浏览器安装bun playwright install运行测试仓库根目录 package.json 中定义了完整的 e2e 脚本底层统一是pnpm exec playwright testbun/yarn/pnpm仅是包管理器前缀差异# 交互式 UI 模式运行编写/调试测试最常用 bun test:e2e:ui # CI 模式运行等价于 test:e2e yarn test:e2e:ci # 有头模式能看到浏览器窗口方便观察执行过程 yarn test:e2e:headed # 调试模式--debug配合 Playwright Inspector yarn test:e2e:debug # 查看详细 HTML 报告 yarn playwright show-report tests/playwright-report这些脚本分别对应package.json中的test:e2e:ui、test:e2e:ci、test:e2e:headed、test:e2e:debug与test:e2e:reporter。test:e2e:ci在 CI 环境下会启用fullyParallel、retries: 1、maxFailures: 10、workers: 18见 playwright.config.ts并行跑满整机资源同时限制失败数量防止雪崩。本地开发时这些参数关闭便于逐个定位问题。更新截图基准当功能有意变更导致渲染结果改变时可以一键用当前渲染结果覆盖旧基准图pnpm test:e2e:update该命令等价于playwright test --update-snapshots但注意默认带-g shouldUpdateThis过滤请按实际需要去掉过滤条件后运行。手动启动 Viewer 以加速开发默认情况下运行测试会自动先执行yarn start仓库根 package.json 中start指向pnpm run dev即ohif/app的dev:viewer启动 Viewer再跑测试。如果希望手动启动、让 Playwright 复用已有服务可以直接先启动应用再运行测试——配置中的reuseExistingServer: !process.env.CI会跳过webServer的启动步骤直接连到已运行的服务上显著缩短测试迭代周期。需要说明的是官方文档写作时 Viewer 默认在http://localhost:3000当前仓库的 e2e 配置则统一使用OHIF_PORT3335具体端口请以 playwright.config.ts 中的baseURL与实际启动命令为准。编写第一个测试指定研究实例与 ModeOHIF 的测试通常按功能 数据集组织。在test.beforeEach中通过visitStudy打开指定研究实例是几乎所有测试的标准入口。visitStudy.ts 提供了两个工具visitStudy(page, studyInstanceUID, mode, delay, datasources)最简形式visitStudyOptions(page, studyInstanceUID, options)结构化选项形式支持mode、delay、datasources、customization。从实现看visitStudy最终构造的 URL 是/mode/datasources?StudyInstanceUIDsstudyInstanceUID并依次等待domcontentloaded与networkidle后再按需waitForTimeout(delay)。例如使用 StudyInstanceUID2.16.840.1.114362.1.11972228.22789312658.616067305.306.2和Basic Viewer模式import { test } from playwright/test; import { visitStudy, checkForScreenshot, screenShotPaths } from ./utils/index.js; test.beforeEach(async ({ page }) { const studyInstanceUID 2.16.840.1.114362.1.11972228.22789312658.616067305.306.2; const mode Basic Viewer; await visitStudy(page, studyInstanceUID, mode); }); test.describe(Some Test, async () { test(should do something., async ({ page }) { // Your test code here... }); });真实测试如 tests/ArrowAnnotate.spec.ts的写法与此一致只是mode通常取viewer对应Basic Viewer的 URL 形式并传入2000毫秒的 delay 等待影像渲染稳定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); });visitStudyOptions还允许附加查询参数customization会被编码为customization...例如Customization.spec.ts通过?customizationveterinary/veterinaryOverlay加载兽医覆盖层配置datasources用于切换数据源默认ohif。测试数据本身由pnpm test:data命令通过 git submodule 拉取testdata目录提供。视觉回归用 checkForScreenshot 验证渲染结果提前规划截图路径截图是 OHIF 端到端测试最重要的断言手段。截图的清单集中在 tests/utils/screenShotPaths.ts测试执行前就应规划好每个测试各阶段的截图命名。例如在添加测量之后截图可以这样定义const screenShotPaths { your_test_name: { measurementAdded: measurementAdded.png, measurementRemoved: measurementRemoved.png, }, };仓库中实际的 screenShotPaths.ts 已为length、angle、circle、ellipse、mpr、threeDOnly、segHydration、rtHydration、jumpToMeasurementMPR、splineContourSegmentation等数十个测试预定义了大量路径新增测试时遵循同样的命名模式即可。在测试中截图断言规划好路径后在测试中调用 checkForScreenshot.ts 导出的checkForScreenshot即可import { test } from playwright/test; import { visitStudy, checkForScreenshot, screenShotPaths, } from ./utils/index.js; test.beforeEach(async ({ page }) { const studyInstanceUID 2.16.840.1.114362.1.11972228.22789312658.616067305.306.2; const mode Basic Viewer; await visitStudy(page, studyInstanceUID, mode); }); test.describe(Some test, async () { test(should do something, async ({ page }) { // Your test code here to add a measurement await checkForScreenshot( page, page, screenShotPaths.your_test_name.measurementAdded ); }); });checkForScreenshot底层封装了 Playwright 的expect(locator).toHaveScreenshot()关键行为如下首次运行自动生成基准图测试会失败第一次运行因没有基准图而报错但同时会在tests/screenshots/chromium/spec 名/截图名.png下生成基准图当前配置只启用chromium项目官方文档中提到的firefox/与webkit/目录对应旧版多浏览器配置。请人工核验生成的基准图确实正确再提交进仓库作为后续比对的基准。内置重试机制默认attempts 10、delay 1250毫秒截图比对不通过时自动重试避免 GPU 渲染抖动导致偶发失败若始终失败抛出原始错误。可调参数见CheckForScreenshotPropsmaxDiffPixelRatio默认0.02允许的最大差异像素比例、threshold默认0.05逐像素色差阈值、normalizedClip归一化裁剪区域按 locator 包围盒比例裁剪、fullPage整页截图、beforeAttempt每次尝试前回调。测试中若预期画面会逐渐变化应优先调大attempts与delay而不是盲目waitForTimeout后一次性截图。中间产物清理每次重试前会自动删除name-actual.png、name-diff.png、name-expected.png等中间产物保证tests/screenshots目录干净。视口级截图针对具体视口viewport的渲染断言OHIF 还提供了checkForViewportScreenshottests/utils/checkForViewportScreenshot.ts。它把截图范围限定在视口 pane 内并在每次捕获前通过viewport.hideAllText()隐藏全部叠加文字覆盖层、标注文字、方位标记避免字体渲染差异干扰像素比对捕获结束后再viewport.showAllText()恢复。典型用法见 tests/ArrowAnnotate.spec.tsawait checkForViewportScreenshot({ page, viewport: activeViewport, screenshotPath: screenShotPaths.arrowAnnotate.arrowAnnotateDisplayedCorrectly0, });等待渲染完成再截图在 tests/utils/waitForViewportsRendered.ts 中提供了基于 Cornerstone3D 渲染状态机的一组等待函数waitForAnyViewportNeedsRender(page)等待至少一个视口进入needsRender状态waitForViewportsRendered(page, options)等待所有视口viewportStatus rendered且默认所有关联 volume 的loadStatus.loaded为真waitForViewportRenderCycle(page)两者的组合——先等needsRender再等renderedwaitForPaintToSettle(page)再等两个requestAnimationFrame周期加 150ms 空闲确保 GPU 把最终帧真正提交settle参数默认开启。这正是渲染完成后截图像素稳定的工程化答案checkForScreenshot处理像素级的轻微抖动而这些等待函数从渲染管线层面保证截图发生在绘制已落定的时刻两者配合可大幅降低截图测试的 flakiness。需要留意的是这些函数依赖命令模块在交互时同步把视口状态置为needsRender个别操作如切换挂载协议目前不满足该前提需用显式延迟替代。模拟鼠标交互拖拽、点击与双击医学影像工具的核心交互是鼠标绘制。OHIF 封装了多组模拟工具全部从 tests/utils/index.ts 统一导出。拖拽simulateDrag官方文档提及的simulateDrag在当前的仓库中对应 simulateDragOnElement.ts 导出的simulateNormalizedDragOnElement。它最大的优势是使用归一化坐标所有点位都按元素包围盒boundingBox的比例给出0~1内部换算成绝对像素天然避免拖出元素边界的问题也不需要维护易错且难读的绝对坐标。例如在cornerstone-canvas上模拟一次拖拽import { visitStudy, checkForScreenshot, screenShotPaths, simulateNormalizedDragOnElement, } from ./utils/index.js; test.beforeEach(async ({ page }) { const studyInstanceUID 2.16.840.1.114362.1.11972228.22789312658.616067305.306.2; const mode Basic Viewer; await visitStudy(page, studyInstanceUID, mode); }); test.describe(Some Test, async () { test(should do something.., async ({ page }) { const locator page.locator(.cornerstone-canvas); await simulateNormalizedDragOnElement({ locator, start: { x: 0.3, y: 0.4 }, end: { x: 0.7, y: 0.6 }, }); }); });simulateNormalizedDragOnElement支持buttonleft | right | middle默认左键、delay每步间隔毫秒默认 50、steps每段中间步数默认 10值越大拖拽轨迹越平滑、mouseUp是否在终点松开默认 true。其底层simulateNormalizedPathDragOnElement还支持沿多点路径连续拖拽mousedown在路径起点平滑滑过所有中间点mouseup在终点适合绘制 ROI、套索这类多段轨迹。点击与双击tests/utils/simulateClicksOnElement.ts 提供了一系列点击工具simulateClicksOnElement({ locator, points, button })在多个绝对坐标点上依次点击simulateDoubleClickOnElement({ locator, point })绝对坐标双击如 ArrowAnnotate 双击标注重新打开文本编辑框simulateNormalizedClickOnElement({ locator, normalizedPoint, button })归一化坐标单击simulateNormalizedClicksOnElement(...)归一化坐标多次点击simulateNormalizedDoubleClickOnElement(...)归一化坐标双击。这些工具与simulateNormalizedDragOnElement一样按包围盒比例换算坐标配合 tests/utils/mouseUtils.ts 的鼠标位置追踪getMousePosition、initializeMousePositionTracker可以精确断言鼠标落点。深入应用内部page.evaluate 访问 Services、Managers 与 Cornerstone端到端测试有时需要越过 UI、直接驱动应用内部状态。OHIF 支持在测试中通过page.evaluate访问 Cornerstone3D、Services、CommandManager 等运行时对象。AppTypes.Test类型定义于 platform/core/src/types/AppTypes.ts 的namespace AppTypes中标注了window上可用的句柄services、commandsManager、extensionManager、config。例如想直接调用uiNotificationService弹出一条 UI 通知await page.evaluate(({ services }: AppTypes.Test) { const { uiNotificationService } services; uiNotificationService.show({ title: Test, message: This is a test, type: info, }); }, await page.evaluateHandle(window));注意第二参数await page.evaluateHandle(window)它将浏览器window对象作为 handle 传入使回调的第一个参数即window从而解构出services。这类能力在真实测试中广泛使用访问 Cornerstone3D 渲染引擎在 tests/JumpToMeasurementMPR.spec.ts 等测试中通过window.cornerstone.getRenderingEngines()获取渲染引擎与视口进而调用viewport.getActors()、读取viewportStatuswaitForViewportsRendered.ts 内部也正是依赖window.cornerstone轮询渲染状态读写自定义配置addOHIFGlobalCustomizationstests/utils/OHIFConfiguration.ts通过(window as any).services?.customizationService.setGlobalCustomization(...)在运行时动态注入定制项断言标注统计getAnnotationStats、getSUV、getTMTVModalityUnit、getViewportCanvasStats等工具均基于page.evaluate读取窗口内对象或 canvas 像素数据见 tests/utils/index.ts 的导出清单。page.evaluate的返回值如注解 UID 列表、统计数值可以直接参与断言这让端到端测试既能验证用户看到什么也能验证内部状态是什么。测试工程化细节预热、Fixture 与 e2e 配置套件级预热globalSetupglobalSetup.ts 在整套测试开始前用chromium.launch()打开一次viewer/ohif?StudyInstanceUIDs1.3.6.1.4.1.14519...等待[data-cyLayout]可见后关闭。目的是预热开发/预览服务器触发 Rspack 首次缓慢的懒编译、填充浏览器与编解码器缓存、拉取研究元数据否则第一个正式 spec 极易因冷启动超时冷服务器编译而失败。该步骤不做任何断言。自定义 Fixture 与 Page Objecttests/utils/fixture.ts 用test.extend扩展了 Playwright 的test与expect来自playwright-test-coverage保证覆盖率采集与断言可用并注入了两个关键能力全局自动 fixture_applyGlobalE2EOHIFBaseline每个测试开始前自动调用addOHIFConfiguration(page, {})通过page.addInitScript覆写window.config默认关闭视口滚动条的各种填充/加载图案显示见 OHIFConfiguration.ts 的DEFAULT_E2E_OHIF_CUSTOMIZATIONS消除渲染差异一组 Page ObjectDOMOverlayPageObject、mainToolbarPageObject、leftPanelPageObject、rightPanelPageObject、viewportPageObject、notFoundStudyPageObject实现定义在 tests/pages把点击工具栏图标选中某个测量视口内点击坐标等操作封装为可复用方法。测试通过解构 fixture 参数直接使用例如ArrowAnnotate.spec.ts中的mainToolbarPageObject.measurementTools.arrowAnnotate.click()。专用的 e2e 应用配置Playwright 的webServer以 platform/app/public/config/e2e.js 作为应用配置启动。该配置为测试量身定制modes: [ohif/mode-test]只挂载测试模式加快构建与启动defaultDataSourceName: e2e并定义了e2e、ohif、dicomweb、orthanc等多个数据源其中e2e指向staticWado: true的/viewer-testdata由testdata子模块提供静态 WADO 数据wadoUriRoot/qidoRoot/wadoRoot统一指向本地customizationUrlPrefixes允许测试通过?customization加载额外定制showStudyList: true、investigationalUseDialog.option: never跳过知情同意弹窗、maxNumberOfWebWorkers: 3等共同保证测试环境干净可控。此外webServer命令带有COVERAGEtrue与nyc配合pnpm test:e2e:coverage可产出端到端覆盖率报告。用 Playwright VSCode 扩展录制与调试测试在 VSCode 中搜索并安装Playwright官方扩展可以获得图形化的测试运行器、用鼠标拾取定位器locator以及录制新测试等能力。录制的流程通常是在扩展面板点击Record new test→ 在自动打开的浏览器中手动操作 Viewer切换工具、绘制测量等→ 扩展自动生成对应的 Playwright 测试代码再结合本指南的visitStudy、screenShotPaths、checkForScreenshot等工具做规范化改造与断言增强。UI 模式bun test:e2e:ui同样提供时间轴回放、步骤断点、实时 locator 校验是日常编写与调试 OHIF 测试的首选环境。结语OHIF Viewer 的 Playwright 测试体系可以概括为一条清晰的链路用visitStudy打开指定研究实例 → 用simulateNormalizedDragOnElement/点击工具模拟用户交互 → 用waitForViewportRenderCycle等待渲染落定 → 用checkForScreenshot做像素级视觉断言 → 必要时用page.evaluate直连 Services 与 Cornerstone3D 校验内部状态最后由 playwright.config.ts 与 package.json 中的test:e2e:ci等脚本一键接入 CI。截图基准由首次运行自动生成、人工核验后入库的流程管理配合--update-snapshots处理有意的渲染变更。这套既有上层交互模拟、又有底层状态验证的分层测试方案是保障 OHIF 影像渲染与交互质量、并在多扩展/多模式架构下持续演进的基石。【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表