【问题标题】:How do I properly mock a DOM so that I can test a Vue app with Jest that uses Xterm.js?如何正确模拟 DOM,以便可以使用 Jest 测试使用 Xterm.js 的 Vue 应用程序?
【发布时间】:2019-10-19 12:47:53
【问题描述】:

我有一个渲染 Xterm.js 终端的 Vue 组件。

Terminal.vue

<template>
  <div id="terminal"></div>
</template>

<script>
import Vue from 'vue';
import { Terminal } from 'xterm/lib/public/Terminal';
import { ITerminalOptions, ITheme } from 'xterm';

export default Vue.extend({
  data() {
    return {};
  },
  mounted() {
    Terminal.applyAddon(fit);
    this.term = new Terminal(opts);
    this.term.open(document.getElementById('terminal'));    
  },
</script>

我想测试这个组件。

Terminal.test.js

import Terminal from 'components/Terminal'
import { mount } from '@vue/test-utils';

describe('test', ()=>{
  const wrapper = mount(App);
});

当我在这个测试文件上运行jest 时,我得到这个错误:

TypeError: Cannot set property 'globalCompositeOperation' of null

      45 |     this.term = new Terminal(opts);
    > 46 |     this.term.open(document.getElementById('terminal'));

深入堆栈跟踪,我可以看到它与 Xterm 的 ColorManager 有关。

  at new ColorManager (node_modules/xterm/src/renderer/ColorManager.ts:94:39)
  at new Renderer (node_modules/xterm/src/renderer/Renderer.ts:41:25)

如果我查看他们的代码,我可以看到一个相对混乱的事情:

xterm.js/ColorManager.ts

  constructor(document: Document, public allowTransparency: boolean) {
    const canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = 1;
    const ctx = canvas.getContext('2d');
    // I would expect to see the "could not get rendering context"
    // error, as "ctx" shows up as "null" later, guessing from the
    // error that Jest caught
    if (!ctx) {
      throw new Error('Could not get rendering context');
    }
    this._ctx = ctx;
    // Somehow this._ctx is null here, but passed a boolean check earlier?
    this._ctx.globalCompositeOperation = 'copy';
    this._litmusColor = this._ctx.createLinearGradient(0, 0, 1, 1);
    this.colors = {
      foreground: DEFAULT_FOREGROUND,
      background: DEFAULT_BACKGROUND,
      cursor: DEFAULT_CURSOR,
      cursorAccent: DEFAULT_CURSOR_ACCENT,
      selection: DEFAULT_SELECTION,
      ansi: DEFAULT_ANSI_COLORS.slice()
    };
  }

我不太清楚canvas.getContext 是如何返回通过布尔检查(if(!ctx))但后来在同一变量上导致cannot set globalCompositeOperation of null 错误的。

我对如何成功进行模拟渲染并因此测试这个组件感到非常困惑 - 在 xterm 自己的测试文件中,他们似乎正在使用 jsdom 创建一个假 DOM:

xterm.js/ColorManager.test.ts

  beforeEach(() => {
    dom = new jsdom.JSDOM('');
    window = dom.window;
    document = window.document;
    (<any>window).HTMLCanvasElement.prototype.getContext = () => ({
      createLinearGradient(): any {
        return null;
      },

      fillRect(): void { },

      getImageData(): any {
        return {data: [0, 0, 0, 0xFF]};
      }
    });
    cm = new ColorManager(document, false);
});

但我相信在幕后,vue-test-utils 也在使用jsdom 创建一个假 DOM。此外,文档表明 mount 函数既附加又呈现 vue 组件。

创建一个包含已安装和渲染的 Vue 组件的 Wrapper。

https://vue-test-utils.vuejs.org/api/#mount

我怎样才能成功地模拟 DOM,以便我可以使用 Jest 测试实现 Xterm.js 的 Vue 组件?

【问题讨论】:

    标签: javascript vue.js jestjs vue-test-utils xtermjs


    【解决方案1】:

    上面的答案很好,这将是我的解决方案,但由于我使用的是 react-scripts,我真的不想弹出(不支持 testEnvironment 配置设置)。因此,我查看了 react-scripts 的源代码,我是如何潜入并覆盖 testEnvironment 的。

    https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/scripts/test.js

    第 124 行看起来很有趣

    resolvedEnv = resolveJestDefaultEnvironment(`jest-environment-${env}`);
    

    所以我想到了一个绝妙的主意,没有双关语,将 --env=jsdom-fourteen 作为命令行参数。我的 CI 命令现在看起来像这样

    cross-env CI=true react-scripts test --coverage --env=jsdom-fourteen --testResultsProcessor=jest-teamcity-reporter
    

    它神奇地起作用了:)。

    我在 src 文件夹中也有 setupTests.js 文件,我在其中导入 jest-canvas-mockjest-environment-jsdom-fourteen 但在 --env hacky 之前选项测试吐出上面提到的 insertAdjacentElement。

    Obvs 这很 hacky,它会在某个时候崩溃,但现在还好,希望 Jest 很快就会开始支持 JSDOM 14。

    【讨论】:

      【解决方案2】:

      这有多种原因。

      首先,正如我所怀疑的那样,Jest js 在底层使用了 jsdom。

      Jsdom 不支持开箱即用的 canvas DOM api。首先,您需要jest-canvas-mock

      npm install --save-dev jest-canvas-mock

      然后,您需要将其添加到您的 jest 配置的 setupFiles 部分。我的在package.json,所以我这样添加:

      package.json

      {
        "jest": {
          "setupFiles": ["jest-canvas-mock"]
        }
      }
      

      然后,我收到了关于 insertAdjacentElement DOM 元素方法的错误。具体来说,错误是:

      [Vue warn]: Error in mounted hook: "TypeError: _this._terminal.element.insertAdjacentElement is not a function"
      

      这是因为 jest 使用的 jsdom 版本,截至今日,11.12.0

      npm ls jsdom

      └─┬ jest@24.8.0
        └─┬ jest-cli@24.8.0
          └─┬ jest-config@24.8.0
            └─┬ jest-environment-jsdom@24.8.0
              └── jsdom@11.12.0 
      

      通过stackoverflow的帮助,我发现在11.12.0版本,jsdom并没有实现insertAdjacentElement。但是,更新版本的 jsdom 实现了 insertAdjacentElement back in July of 2018.

      Efforts to convince the jest team 使用更新版本的 jsdom 失败。他们不愿意放弃 node6 的兼容性,直到绝对的最后一秒(他们在 4 月份声称),或者根本不想再实现 jsdom,并且如果他们想要的话,建议人们分叉他们自己的 repo 版本特征。

      幸运的是,您可以手动设置 jsdom jest 使用的版本。

      首先,安装jest-environment-jsdom-fourteen 包。

      npm install --save jest-environment-jsdom-fourteen

      然后,您需要修改您的 jest 配置的 testEnvironment 属性。所以,现在我的笑话配置看起来像:

      package.json

        "jest": {
          "testEnvironment": "jest-environment-jsdom-fourteen",
          "setupFiles": ["jest-canvas-mock"]
        }
      

      现在,我可以运行测试而不会出错。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-17
        • 2020-03-07
        • 1970-01-01
        • 2021-10-14
        • 1970-01-01
        • 1970-01-01
        • 2021-01-12
        • 2020-05-28
        相关资源
        最近更新 更多