4

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

终端.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明显地返回通过布尔检查(at if(!ctx))但后来cannot set globalCompositeOperation of null在同一个变量上导致错误的东西。

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

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. 此外,文档表明该mount函数既附加又呈现 vue 组件。

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

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

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

4

2 回答 2

6

这有多种原因。

首先,正如我所怀疑的那样,Jest js 在后台使用 jsdom。

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

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

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

包.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。然而,更新版本的 jsdominsertAdjacentElement 早在 2018 年 7 月就实施了。

说服 jest 团队使用更新版本的 jsdom 的努力失败了。他们不愿意放弃 node6 的兼容性,直到绝对的最后一秒(他们在 4 月声称),或者根本不想再实现 jsdom,并且建议人们在他们想要的情况下分叉他们自己的 repo 版本特征。

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

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

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

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

包.json

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

现在,我可以毫无错误地运行测试。

于 2019-06-04T18:43:49.470 回答
2

上面的答案很好,这将是我的解决方案,但由于我使用的是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。

于 2019-08-09T08:35:14.360 回答