测试包装的 vuetify 自动完成组件 v 菜单未在 html 中打开

Posted

技术标签:

【中文标题】测试包装的 vuetify 自动完成组件 v 菜单未在 html 中打开【英文标题】:Testing a wrapped vuetify autocomplete component v menu does not open in html 【发布时间】:2021-09-15 05:45:26 【问题描述】:

我想通过vue-test-utils 测试我的自定义组件。在这种情况下,我使用 vuetify autocomplete 组件。我只是用 div 包装并提取为自定义组件。到目前为止没有什么特别的。

这个组件确实在开发中工作。我没有问题

我使用这个组件如下图。

<my-component
  id="brand-dropdown"
  ref="brand-dropdown"
  :items="brands"
  labelKey="product.brand"
  item-text="name"
  item-value="name"
  v-model="product.brandName"/>

我的自定义组件如下所示:

<template>
  <div class="comboContainer">
    <v-autocomplete
      class="product-combobox"
      ref="complete"
      :items="localItems"
      append-icon="$dropdownArrow"
      outlined
      dense
      hide-details
      color="#999999"
      :item-text="itemText"
      :item-value="itemValue"
      :filter="autoCompleteFilter"
      v-model="selected"
      @change="onListItemSelected">
      <template slot="label">
        <span>$t(labelKey)</span>
      </template>
      <template slot="prepend-inner" v-if="showSearchIcon">
        <div class="d-flex align-center justify-center"
             style="height: 25px;">
          <img src="~/assets/icons/search/search.png"/>
        </div>
      </template>
      <template v-slot:no-data>
        <v-list-item id="noMatchText">
          <v-list-item-title>
            <div :no-data-item="dataTestId">
               $t('selection.noMatchFound') 
            </div>
          </v-list-item-title>
        </v-list-item>
      </template>
      <template v-slot:item=" item ">
        <v-tooltip top color="transparent">
          <template v-slot:activator=" on, attrs ">
            <div
              v-on="item[itemText].length >36?on:null"
              class="combobox-item comboboxOverFlow"
              :data-testid="itemDataTestIdPrefix+item.id"
              :parent-list="dataTestId">
              <span> item[itemText] </span>
            </div>
          </template>
          <div class="popup-rectangle">
            <span> item[itemText] </span>
          </div>
        </v-tooltip>
      </template>
    </v-autocomplete>
  </div>
</template>

<script>
export default 
  props: 
    items: 
      type: Array,
      default: [],
    ,
    labelKey: 
      type: String,
      default: '',
    ,
    itemText: 
      type: String,
      default: '',
    ,
    itemValue: 
      type: String,
      default: '',
    ,
    value: 
      type: [Number, String, Array],
    ,
    disabled: 
      type: Boolean,
      default: false,
    
    shouldTriggerWatch: 
      type: Boolean,
      default: false,
    
  ,
  data() 
    return 
      selected: this.value,
      showSearchIcon: false,
    ;
  ,
  watch: 
    value: 
      immediate: true,
      handler(val) 
        if (this.shouldTriggerWatch) 
          this.selected = val;
          this.$emit('input', this.selected);
        
      ,
    ,
  ,
  computed: 
    localItems() 
      if (this.items && this.items.length) 
        return this.items.map(x => (
          ...x,
          displayName: x.lang_key ? this.$t(x.lang_key) : x.name,
        ));
      
      return [];
    ,
  ,
  methods: 
    onListItemSelected() 
      this.$emit('input', this.selected);
    ,
    autoCompleteFilter(item, queryText, itemText) 
      const re = new RegExp(`(\\s+|^)($queryText.toLocaleLowerCase())(.*|$)`);
      return re.test(itemText.toLocaleLowerCase());
    ,
  ,
;

我要测试三个测试用例。

    当我传递空列表时,应呈现为空下拉列表并呈现无数据槽。
      <template v-slot:no-data>
        <v-list-item id="noMatchText">
          <v-list-item-title>
            <div :no-data-item="dataTestId">
               $t('selection.noMatchFound') 
            </div>
          </v-list-item-title>
        </v-list-item>
      </template>
    当我通过带有测试数据的填充列表时,我想测试正确呈现的所有项目
     <template v-slot:item=" item ">
        <v-tooltip top color="transparent">
          <template v-slot:activator=" on, attrs ">
            <div
              v-on="item[itemText].length >36?on:null"
              class="combobox-item comboboxOverFlow"
              :data-testid="itemDataTestIdPrefix+item.id"
              :parent-list="dataTestId">
              <span> item[itemText] </span>
            </div>
          </template>
          <div class="popup-rectangle">
            <span> item[itemText] </span>
          </div>
        </v-tooltip>
      </template>
    当我搜索时,我想提供我的过滤功能按预期工作。并且自动完成会根据过滤后的值呈现项目。

这是我的测试文件。

function mountComponent(options) 
  return mount(ComboBox, 
    vuetify: new Vuetify(),
    sync: false,
    mocks: 
      $t: key => key,
      $i18n:  locale: 'en' ,
    ,
    ...options,
  );


describe('Combobox unit tests', () => 
  beforeEach(() => 
    document.body.setAttribute('data-app', 'true');
  );

  test('should create component successfully', () => 
    const wrapper = mountComponent( items: [] );
    expect(wrapper.exists()).toBeTruthy();
  );
  test('should list zero items if the item list is empty', async () => 
    const wrapper = mountComponent(
      propsData: 
        items: [],
        labelKey: 'labelKey',
        dataTestId: 'test-dropdown',
        itemText: 'name',
        itemValue: 'id',
      ,
    );

    const autocomplete = wrapper.find('.product-combobox');
    const autocompleteControls = autocomplete.find('.v-input__slot');
    autocompleteControls.trigger('click');

    await wrapper.vm.$nextTick();
    await flushPromises();

    **// v menu cant opened !!!!**
  );
  test('should list third items correctly', async () => 
    const testItems = [ name: 'item1', id: 1 ,  name: 'item2', id: 2 ,  name: 'item3', id: 3 ];
    const wrapper = mountComponent(
      attachToDocument: true,
      propsData: 
        eagerProp: true,
        items: testItems,
        dataTestId: 'test-dropdown',
        itemDataTestIdPrefix: 'test-dropdown-item-',
        itemText: 'name',
        itemValue: 'id',
        value: null,
      ,
    );

    const slot = wrapper.find('.v-input__slot');
    const input = wrapper.find('input');

    // Focus input should only focus
    input.trigger('focus');

    expect(wrapper.vm.$children[0].isFocused).toBe(true);
    expect(wrapper.vm.$children[0].menuCanShow).toBe(true);
    expect(wrapper.vm.$children[0].isMenuActive).toBe(false);

    slot.trigger('click');

    expect(wrapper.vm.$children[0].isMenuActive).toBe(true);
    expect(wrapper.vm.$children[0].menuCanShow).toBe(true);

    wrapper.setProps( searchInput: 'foo' );

    expect(wrapper.vm.$children[0].isMenuActive).toBe(true);
    expect(wrapper.vm.$children[0].menuCanShow).toBe(true);

    // v menu cant opened !!!!
    // you think these expects is unnecesary. you are right I just explore this component 
    // if I success I'll delete all of them
  );
  test('should filter autocomplete search results', async () => 
    
  );
);

我无法在测试环境中打开 v-menu。

我尝试发出事件并触发('click')

当我控制台日志wrapper.html() 时,我看不到 v 菜单已打开并且所有项目都以 html 呈现。输出如下所示。

<div class="comboContainer">
   <div class="v-input product-combobox v-input--hide-details v-input--dense theme--light v-text-field v-text-field--enclosed v-text-field--outlined v-select v-autocomplete">
      <div class="v-input__control">
         <div role="combobox" aria-haspopup="listbox" aria-expanded="false" aria-owns="list-2" class="v-input__slot" style="height: 44px;">
            <fieldset aria-hidden="true">
               <legend style="width: 0px;"><span>​</span></legend>
            </fieldset>
            <div class="v-select__slot">
               <label for="input-2" class="v-label theme--light" style="left: 0px; position: absolute;"><span></span></label><input data-testid="test-dropdown" id="input-2" type="text" autocomplete="off">
               <div class="v-input__append-inner">
                  <div class="v-input__icon v-input__icon--append"><i aria-hidden="true" class="v-icon notranslate material-icons theme--light">$dropdownArrow</i></div>
               </div>
               <input type="hidden">
            </div>
            <div class="v-menu"></div>
         </div>
      </div>
   </div>
</div>

我的问题是物品在哪里? &lt;div class="v-menu"&gt;&lt;/div&gt; 为什么我在 wrapper.html 中看不到。我不使用 shallowMount,我使用 mount。因此,我无法编写涵盖的测试用例。

如何模拟打开的菜单并渲染所有项目并提供一些断言?

我错过了什么?

版本

"@nuxtjs/vuetify": "1.12.1",
"@vue/test-utils": "1.0.0-beta.29",
"vue-jest": "3.0.7"

【问题讨论】:

【参考方案1】:

嗯,可能更好的方法是用一个测试组件模拟和替换 v-autocomplete(只是控制它的值并从中发出假事件)。您不是在开发 Vuetify,因此无需测试组件内部发生的情况。

【讨论】:

以上是关于测试包装的 vuetify 自动完成组件 v 菜单未在 html 中打开的主要内容,如果未能解决你的问题,请参考以下文章

Vuetify v-menu 组件不可见

Vuetify 服务器端自动完成

Vuetify - 在 v-data-table 上包装标题文本

Vuetify 自动完成,接受列表之外的值

Vuetify <v-menu> 中的自动对焦和键盘导航

Vue.js/Vuetify - 在 v-select 中预加载选项时的自动完成问题