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

React集成JointJS组件单元测试报xhtml未定义TypeError

问题:React集成JointJS组件单元测试报错TypeError: Cannot read properties of undefined (reading 'xhtml')

我正在为集成JointJS库的React组件编写单元测试用例,运行时抛出如下错误:

TypeError: Cannot read properties of undefined (reading 'xhtml')

      175 |     graphRef.current = graph;
      176 |     const tree = new joint.layout.TreeLayout({ graph: graph });
    > 177 |     const paper = new joint.dia.Paper({
          |                   ^
      178 |       width: width,
      179 |       height: height,
      180 |       model: graph,

      at child.xhtml [as children] (node_modules/@clientio/rappid/rappid.js:31722:31)
      at result (node_modules/lodash/lodash.js:13708:44)
      at child.result [as renderChildren] (node_modules/@clientio/rappid/rappid.js:18445:34)
      at child.renderChildren (node_modules/@clientio/rappid/rappid.js:31770:15)
      at child.apply [as render] (node_modules/@clientio/rappid/rappid.js:18696:27)
      at child.render [as init] (node_modules/@clientio/rappid/rappid.js:31572:15)
      at child.init [as initialize] (node_modules/@clientio/rappid/rappid.js:18433:15)
      at child.Backbone.View (node_modules/backbone/backbone.js:1310:21)
      at child.call [as constructor] (node_modules/@clientio/rappid/rappid.js:18425:24)
      at new child (node_modules/backbone/backbone.js:2070:41)
      at src/components/Configuration/SiteNavigation/SiteNavigation.tsx:177:19
      at commitHookEffectListMount (node_modules/react-dom/cjs/react-dom.development.js:19731:26)
      at commitPassiveHookEffects (node_modules/react-dom/cjs/react-dom.development.js:19769:11)
      at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:188:14)
      at HTMLUnknownElement.callTheUserObjectsOperation (node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:241:34)
      at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:237:16)
      at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:292:31)
      at flushPassiveEffectsImpl (node_modules/react-dom/cjs/react-dom.development.js:22853:9)
      at unstable_runWithPriority (node_modules/scheduler/cjs/scheduler.development.js:653:12)
      at runWithPriority$1 (node_modules/react-dom/cjs/react-dom.development.js:11039:10)
      at flushPassiveEffects (node_modules/react-dom/cjs/react-dom.development.js:22820:12)
      at Object.<anonymous>.flushWork (node_modules/react-dom/cjs/react-dom-test-utils.development.js:876:10)
      at act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:994:9)
      at render (node_modules/@testing-library/react/dist/pure.js:97:26)

以下是组件初始渲染时,在useEffect钩子中声明graph和paper对象的代码:

const tree = new joint.layout.TreeLayout({ graph: graph });
const paper = new joint.dia.Paper({
  width: width,
  height: height,
  model: graph,
  gridSize: 10,
  perpendicularLinks: false,
  drawGrid: { name: "mesh", args: { color: "#D3D4D5", scaleFactor: 4 } },
  background: {
    color: "rgba(0, 255, 0, 0.0)",
  },
  interactive: true,
});

解决方案

这个错误源于joint.dia.Paper在测试环境(如jsdom)中缺少必要的DOM挂载节点或环境支持,可通过以下方式解决:

  • 给Paper指定挂载容器
    创建Paper实例时,添加el属性指向一个真实DOM元素。测试环境中可提前创建并挂载容器,测试后清理:

    // 在useEffect内或测试用例中添加
    const container = document.createElement('div');
    document.body.appendChild(container);
    
    const paper = new joint.dia.Paper({
      el: container, // 新增挂载容器配置
      width: width,
      height: height,
      model: graph,
      // 其他原有配置
    });
    
    // 测试结束后清理(可放在useEffect的清理函数中)
    return () => {
      document.body.removeChild(container);
      paper.remove();
    };
    
  • Mock JointJS的Paper类
    若单元测试无需验证Paper渲染细节,直接Mockjoint.dia.Paper避免环境问题(以Jest为例):

    jest.mock('@clientio/rappid', () => {
      const original = jest.requireActual('@clientio/rappid');
      return {
        ...original,
        dia: {
          ...original.dia,
          Paper: jest.fn().mockReturnValue({
            // 模拟业务需要的Paper方法,比如remove、render等
            remove: jest.fn(),
            render: jest.fn()
          })
        },
        layout: {
          TreeLayout: jest.fn()
        }
      };
    });
    
  • 优化测试环境DOM配置
    确保jsdom等测试环境支持SVG特性,在Jest配置文件中添加:

    {
      "testEnvironmentOptions": {
        "resources": "usable",
        "runScripts": "dangerously"
      }
    }
    

内容的提问来源于stack exchange,提问作者Deepthy G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:37:33