You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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中监听这个事件,确保所有交互操作都在图像加载完成后执行。

具体实现步骤

  1. 优化组件中Viewer实例的暴露逻辑
    避免重复赋值,确保仅在开发环境且Cypress运行时才暴露实例:

    // 组件useEffect内的代码调整
    if (window.Cypress && DEV && !window.osdViewer) {
      window.osdViewer = viewerRef.current;
    }
    
  2. 自定义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);
            });
          }
        });
      });
    });
    
  3. 修改测试代码,确保操作在图像加载后执行
    在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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 10:34:53