【问题标题】:Why won't Trix editor mount in Vue component when running tests with Jest?使用 Jest 运行测试时,为什么 Trix 编辑器不会安装在 Vue 组件中?
【发布时间】:2019-09-18 07:19:44
【问题描述】:

我构建了一个简单的 Vue 组件来包装 Trix 编辑器。我正在尝试为其编写测试,但 Trix 似乎没有正确安装,并且没有像在浏览器中那样生成工具栏元素。我正在使用 Jest 测试运行器。

TrixEdit.vue

<template>
  <div ref="trix">
    <trix-editor></trix-editor>
  </div>
</template>

<script>
import 'trix'

export default {
  mounted() {
    let el = this.$refs.trix.getElementsByTagName('trix-editor')[0]
    // HACK: change the URL field in the link dialog to allow non-urls
    let toolbar = this.$refs.trix.getElementsByTagName('trix-toolbar')[0]
    toolbar.querySelector('[type=url]').type = 'text'

    // insert content
    el.editor.insertHTML(this.value)

    el.addEventListener('trix-change', e => {
      this.$emit('input', e.target.innerHTML)
    })
  }
}
</script>

TrixEdit.spec.js

import { mount, shallowMount, createLocalVue } from '@vue/test-utils'
import TrixEdit from '@/components/TrixEdit.vue'

const localVue = createLocalVue()
localVue.config.ignoredElements = ['trix-editor']

describe('TrixEdit', () => {
  describe('value prop', () => {
    it('renders text when value is set', () => {
      const wrapper = mount(TrixEdit, {
        localVue,
        propsData: {
          value: 'This is a test'
        }
      })

      expect(wrapper.emitted().input).toEqual('This is a test')
    })
  })
})

expect() 失败并出现以下错误

    Expected value to equal:
      "This is a test"
    Received:
      undefined

    at Object.toEqual (tests/unit/TrixEdit.spec.js:19:39)

为什么 Trix 在我的测试中没有初始化?

【问题讨论】:

  • 你能分享有问题的回购吗?
  • @hackape 不幸的是它是私人的。
  • mocha 单元测试在 nodejs 运行时运行,而 trix 仅在浏览器运行时运行。这就是它无法在单元测试中初始化的原因。
  • @hackape 这是开玩笑,不是摩卡。
  • 好吧,mocha 还是 jest,问题都一样,运行时的区别

标签: unit-testing vue.js vuejs2 jestjs vue-component


【解决方案1】:

trix-editor 没有挂载主要是因为MutationObserver 在 JSDOM 11 中不受支持,并且attachToDocument 没有被使用。下面描述的测试中还有其他几个错误。

GitHub demo w/issues fixed


缺少MutationObserverwindow.getSelection

Vue CLI 3.7.0 使用 JSDOM 11,它不支持 MutationObserver,自定义元素 polyfill 需要它来触发 connectedCallback。该生命周期挂钩通常会调用 trix-editor 的初始化,这将创建您的测试尝试查询的 trix-toolbar 元素。

解决方案 1: 在您的测试中,导入 mutationobserver-shim before TrixEdit.vue 和存根 window.getSelection(由 trix 调用,目前 JSDOM 不支持):

import 'mutationobserver-shim' // <-- order important
import TrixEdit from '@/components/TrixEdit.vue'

window.getSelection = () => ({})

解决方案 2: 在由 setupTestFrameworkScriptFile 配置的 Jest 设置脚本中执行上述操作:

  1. 将以下属性添加到jest.config.js(或package.json 中的jest)中的配置对象:
setupTestFrameworkScriptFile: '<rootDir>/jest-setup.js',
  1. 将以下代码添加到&lt;rootDir&gt;/jest-setup.js
import 'mutationobserver-shim'
window.getSelection = () => ({})

缺少attachToDocument

@vue/test-utils 默认情况下不会将元素附加到文档,因此trix-editor 不会捕获其初始化所需的connectedCallback

解决方法:安装TrixEdit时使用attachToDocument option

const wrapper = mount(TrixEdit, {
  //...
  attachToDocument: true, // <-- needed for trix-editor
})

过早引用trix-editor的编辑器

TrixEdit 错误地假定trix-editor 在挂载时立即初始化,但在触发trix-initialize 事件之前不能保证初始化,因此访问trix-editor 的内部编辑器可能会导致undefined 引用.

解决方案:为调用之前在mounted()中的初始化代码的trix-initialize事件添加event handler

<template>
  <trix-editor @trix-initialize="onInit" />
</template>

<script>
export default {
  methods: {
    onInit(e) {
      /* initialization code */
    }
  }
}
</script>

更改侦听器之前设置的值

初始化代码添加了一个trix-change-event 监听器值已经设置之后,缺少事件触发器。我假设其目的还在于检测第一个初始值设置,以便将其作为input 事件重新发出。

解决方案一:先添加事件监听器:

<script>
export default {
  methods: {
    onInit(e) {
      //...
      el.addEventListener('trix-change', /*...*/)

      /* set editor value */
    }
  }
}
</script>

解决方案2:在模板中使用v-on:trix-change="..."(或@trix-change),这样可以消除上面的设置顺序问题:

<template>
  <trix-editor @trix-change="onChange" />
</template>

<script>
export default {
  methods: {
    onChange(e) {
      this.$emit('input', e.target.innerHTML)
    },
    onInit(e) {
      //...
      /* set editor value */
    }
  }
}
</script>

值设置导致错误

初始化代码用如下代码设置编辑器的值,导致测试出错:

el.editor.insertHTML(this.value) // causes `document.createRange is not a function`

解决方案:使用trix-editorvalue-accessor,它执行等效操作同时避免此错误:

el.value = this.value

【讨论】:

    【解决方案2】:

    我可以确认问题在于 trix lib 中包含的不太理想的聚合物填充物。我做了一个实验来强制应用 polyfill,然后我可以在 chrome 浏览器环境中重现相同的错误TypeError: Cannot read property 'querySelector' of undefined

    进一步调查将范围缩小到 MutationObserver 行为差异,但仍未深入调查。

    复制方式:

    TrixEdit.vue

    <template>
      <div ref="trix">
        <trix-editor></trix-editor>
      </div>
    </template>
    
    <script>
    
    // force apply polymer polyfill
    delete window.customElements;
    document.registerElement = undefined;
    
    import "trix";
    
    //...
    
    </script>
    

    【讨论】:

    • 相当恶心,但我会在星期一测试它,如果一切正常,我会奖励赏金。
    • 值得注意的是,有一个 trix-initialize 事件可能在 mount 上起作用,而不需要 setTimeout
    • 是的,这很恶心......但我没有兴趣再看下去,需要查看太多源代码
    • @Soviut 但是让我做一个有根据的猜测,差异可能源于@vue/test-utilsmounted 真实应用程序中的生命周期钩子可能是异步的。但是从const wrapper = mount()在测试中的使用方式来看,貌似hook是同步调用的。
    • @Soviut 我不禁深入挖掘......这是我迄今为止得到的最新结果。我的猜测是错误的。问题来自trix lib。它使用 WebComponent window.customElements API。在 jsdom 环境中,缺少 API,因此应用了聚合物 polyfill,但它似乎仍然不起作用。
    猜你喜欢
    • 2019-03-19
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 2021-02-02
    • 2021-09-05
    • 2015-04-02
    • 1970-01-01
    相关资源
    最近更新 更多