Почему редактор Trix не монтируется в компоненте Vue при запуске тестов с Jest? - PullRequest
4 голосов
/ 29 апреля 2019

Я создал простой компонент Vue, который обертывает редактор Trix. Я пытаюсь написать тесты для него, но Trix, кажется, не монтирует должным образом и не генерирует элемент панели инструментов, как это происходит в браузере. Я использую Jest Test Runner.

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)

Почему Трикс не инициализируется в моем тесте?

Ответы [ 2 ]

2 голосов
/ 10 мая 2019

trix-editor не монтируется в основном потому, что MutationObserver не поддерживается в JSDOM 11, а attachToDocument не использовалось. В тесте, описанном ниже, было несколько других ошибок.

Демонстрация GitHub с исправленными проблемами


Отсутствует MutationObserver и window.getSelection

Vue CLI 3.7.0 использует JSDOM 11, который не поддерживает MutationObserver, необходимый для полифилла пользовательских элементов для запуска connectedCallback. Этот хук жизненного цикла обычно вызывает инициализацию trix-editor, в результате чего создается элемент trix-toolbar, к которому пытается обратиться ваш тест.

Решение 1: В вашем тесте импортируйте mutationobserver-shim до TrixEdit.vue и заглушку window.getSelection (вызывается trix и в настоящее время не поддерживается JSDOM):

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

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

Решение 2: Выполните вышеуказанные действия в сценарии установки Jest, настроенном с помощью setupTestFrameworkScriptFile:

  1. Добавьте следующее свойство в объект конфигурации в jest.config.js (или jest в package.json):
setupTestFrameworkScriptFile: '<rootDir>/jest-setup.js',
  1. Добавьте следующий код в <rootDir>/jest-setup.js:
import 'mutationobserver-shim'
window.getSelection = () => ({})

Отсутствует attachToDocument

@vue/test-utils по умолчанию не прикрепляет элементы к документу, поэтому trix-editor не перехватывает connectedCallback, необходимый для его инициализации.

Решение: Используйте опцию attachToDocument при монтаже TrixEdit:

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

Преждевременная ссылка на редактор trix-editor

TrixEdit неверно предполагает, что trix-editor сразу инициализируется при монтировании, но инициализация не гарантируется, пока не будет запущено событие trix-initialize, поэтому доступ к внутреннему редактору trix-editor может привести к ссылке undefined .

Решение: Добавить обработчик события для события trix-initialize, которое вызывает код инициализации, ранее указанный в mounted():

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

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

Значение, установленное перед изменением слушателя

Код инициализации добавляет прослушиватель trix-change -event после значение уже установлено, пропуская триггер события. Я предполагаю, что целью было также обнаружить первую настройку начального значения, чтобы переиздать его как событие input.

Решение 1: Сначала добавьте прослушиватель событий:

<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 s value -accessor, который выполняет эквивалентное действие, избегая этой ошибки:

el.value = this.value
2 голосов
/ 04 мая 2019

Я могу подтвердить, что проблема заключается в не очень идеальном полимерном полифилле, включенном в trix lib.Я провел эксперимент, чтобы принудительно применить полифилл, затем я могу воспроизвести ту же ошибку TypeError: Cannot read property 'querySelector' of undefined даже в браузере Chrome env.

Дальнейшие исследования сводят ее к разнице в поведении 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>
...