使用 Jest 运行测试时,为啥 Trix 编辑器不会安装在 Vue 组件中?
Posted
技术标签:
【中文标题】使用 Jest 运行测试时,为啥 Trix 编辑器不会安装在 Vue 组件中?【英文标题】: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,问题都一样,运行时的区别
【参考方案1】:
trix-editor
没有挂载主要是因为MutationObserver
在 JSDOM 11 中不受支持,并且attachToDocument
没有被使用。下面描述的测试中还有其他几个错误。
GitHub demo w/issues fixed
缺少MutationObserver
和window.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 设置脚本中执行上述操作:
-
将以下属性添加到
jest.config.js
(或package.json 中的jest
)中的配置对象:
setupTestFrameworkScriptFile: '<rootDir>/jest-setup.js',
-
将以下代码添加到
<rootDir>/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-editor
的value
-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-utils
。 mounted
真实应用程序中的生命周期钩子可能是异步的。但是从const wrapper = mount()
在测试中的使用方式来看,貌似hook是同步调用的。
@Soviut 我不禁深入挖掘......这是我迄今为止得到的最新结果。我的猜测是错误的。问题来自trix
lib。它使用 WebComponent window.customElements
API。在 jsdom 环境中,缺少 API,因此应用了聚合物 polyfill,但它似乎仍然不起作用。以上是关于使用 Jest 运行测试时,为啥 Trix 编辑器不会安装在 Vue 组件中?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Jest 在测试“Colyseus”游戏时会出现这个错误?
在 Visual Studio Code 编辑器中完全禁用 Jest 测试运行器的自动运行