如何在Cypress中确认OpenSeadragon图像已加载以执行测试?
如何在Cypress测试中判断OpenSeadragon图像已加载
我在React项目中为OpenSeadragon组件编写Cypress单元测试时,遇到了图像未加载完成就执行缩放、重置按钮操作,导致测试失败的问题。需要找到OpenSeadragon判断图像加载完成的方法,以及如何在Cypress中应用该判断保证测试正常执行。
原React组件代码
const { DEV } = import.meta.env; interface OpenSeaDragonImageProps { tileSourcesUrl: string; } const OpenSeaDragonImage = ({ tileSourcesUrl }: OpenSeaDragonImageProps) => { const viewerRef = useRef<OpenSeadragon.Viewer | null>(null); useEffect(() => { viewerRef.current = OpenSeadragon({ id: "osdviewer", tileSources: tileSourcesUrl, zoomInButton: "navControlZoomIn", zoomOutButton: "navControlZoomOut", homeButton: "navControlReset", }); if (window.Cypress && DEV) { window.osdViewer = viewerRef.current; } return () => viewerRef.current?.destroy(); }, [tileSourcesUrl]); return ( <div className={styles.osd}> <div id="osdviewer" className={styles.osdViewer} /> <Stack spacing={4} className={styles.nav}> <ButtonGroup variant="text"> <Button className={styles.navButton}> <NoGrid /> </Button> </ButtonGroup> <ButtonGroup variant="text"> <Button id="navControlReset" className={styles.navButton}> <Reset className={styles.navButtonIcon} /> </Button> </ButtonGroup> <ButtonGroup id="navControl" orientation="vertical" variant="text"> <Button id="navControlZoomIn" className={styles.navButton}> <ZoomIn className={styles.navButtonIcon} /> </Button> <Button id="navControlZoomOut" className={styles.navButton}> <ZoomOut className={styles.navButtonIcon} /> </Button> </ButtonGroup> </Stack> </div> ); }; export default OpenSeaDragonImage;
原Cypress测试代码
import { Viewer } from "openseadragon"; import OpenSeaDragonImage from "./OpenSeaDragonImage"; declare global { interface Window { osdViewer: Viewer; } } context(OpenSeaDragonImage.name, () => { describe("OpenSeaDragon view", () => { beforeEach(() => { cy.mount( <OpenSeaDragonImage tileSourcesUrl="https://openseadragon.github.io/example-images/highsmith/highsmith.dzi" /> ); cy.get(".openseadragon-container").should("exist"); }); it("Should OSD view zoom-in with custom button", () => { cy.window().then((window) => { expect(window.osdViewer.viewport.getZoom()).to.equal(1); window.osdViewer.addHandler("open", function () { cy.get("#navControlZoomIn").as("zoomIn"); cy.get("@zoomIn").click(); cy.get("@zoomIn").then(() => { expect(window.osdViewer.viewport.getZoom()).to.equal(2); }); }); }); }); it("Should OSD view zoom-out with custom button", () => { cy.window().then((window) => { cy.get("#navControlZoomIn").dblclick(); window.osdViewer.addHandler("open", function () { cy.get("#navControlZoomOut").as("zoomOut"); cy.get("@zoomOut").click(); cy.get("@zoomOut").then(() => { expect(window.osdViewer.viewport.getZoom()).to.equal(2); }); }); }); }); it("Should OSD view reset view in custom button", () => { cy.window().then((window) => { cy.get("#navControlZoomIn").dblclick(); window.osdViewer.addHandler("open", function () { cy.get("#navControlReset").as("viewReset"); cy.get("@viewReset").click(); cy.get("@viewReset").then(() => { expect(window.osdViewer.viewport.getZoom()).to.equal(1); }); }); }); }); }); });
解决方案
核心思路
OpenSeadragon提供的open事件是判断图像加载完成的关键——该事件会在图像成功加载并完成初始化后触发。我们可以在Cypress中监听这个事件,确保所有交互操作都在图像加载完成后执行。
具体实现步骤
优化组件中Viewer实例的暴露逻辑
避免重复赋值,确保仅在开发环境且Cypress运行时才暴露实例:// 组件useEffect内的代码调整 if (window.Cypress && DEV && !window.osdViewer) { window.osdViewer = viewerRef.current; }自定义Cypress命令等待图像加载
在cypress/support/commands.ts中添加自定义命令,封装等待逻辑:Cypress.Commands.add('waitForOsdImageLoaded', () => { cy.window().then((win) => { return new Cypress.Promise((resolve) => { // 若图像已加载完成,直接完成等待 if (win.osdViewer.world.getItemAt(0)) { resolve(true); } else { // 监听open事件,触发后完成等待 win.osdViewer.addHandler('open', () => { resolve(true); }); } }); }); });修改测试代码,确保操作在图像加载后执行
在beforeEach中调用自定义命令,统一等待图像加载完成:import { Viewer } from "openseadragon"; import OpenSeaDragonImage from "./OpenSeaDragonImage"; declare global { interface Window { osdViewer: Viewer; } } context(OpenSeaDragonImage.name, () => { describe("OpenSeaDragon view", () => { beforeEach(() => { cy.mount( <OpenSeaDragonImage tileSourcesUrl="https://openseadragon.github.io/example-images/highsmith/highsmith.dzi" /> ); cy.get(".openseadragon-container").should("exist"); // 等待图像加载完成 cy.waitForOsdImageLoaded(); }); it("Should OSD view zoom-in with custom button", () => { cy.window().then((window) => { expect(window.osdViewer.viewport.getZoom()).to.equal(1); cy.get("#navControlZoomIn").click(); // 等待缩放操作完成(可替换为监听viewport-change事件提升精准度) cy.wait(500); expect(window.osdViewer.viewport.getZoom()).to.equal(2); }); }); it("Should OSD view zoom-out with custom button", () => { cy.window().then((window) => { cy.get("#navControlZoomIn").dblclick(); cy.wait(500); expect(window.osdViewer.viewport.getZoom()).to.equal(4); cy.get("#navControlZoomOut").click(); cy.wait(500); expect(window.osdViewer.viewport.getZoom()).to.equal(2); }); }); it("Should OSD view reset view in custom button", () => { cy.window().then((window) => { cy.get("#navControlZoomIn").dblclick(); cy.wait(500); expect(window.osdViewer.viewport.getZoom()).to.equal(4); cy.get("#navControlReset").click(); cy.wait(500); expect(window.osdViewer.viewport.getZoom()).to.equal(1); }); }); }); });
额外说明
- 若需要等待图像完全渲染(所有瓦片加载完成),可替换使用
fully-loaded事件;测试场景下open事件已足够保证交互功能可用。 - 测试中的
cy.wait(500)可替换为监听viewport-change事件,让测试逻辑更精准:cy.window().then((win) => { return new Cypress.Promise((resolve) => { win.osdViewer.addHandler('viewport-change', () => { resolve(true); }); cy.get("#navControlZoomIn").click(); }); });
内容的提问来源于stack exchange,提问作者Mo.
相关产品推荐
相关产品推荐

