测试包装的 vuetify 自动完成组件 v 菜单在 html 中无法打开
Testing a wrapped vuetify autocomplete component v menu does not open in html
在这种情况下,我想通过 vue-test-utils. I use 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>
我的问题是物品在哪里? <div class="v-menu"></div>
为什么我在 wrapper.html 中看不到。我不使用 shallowMount,我使用 mount。因此,我无法编写涵盖的测试用例。
如何模拟打开的菜单并呈现所有项目并提供一些断言?
我错过了什么?
版本
"@nuxtjs/vuetify": "1.12.1",
"@vue/test-utils": "1.0.0-beta.29",
"vue-jest": "3.0.7"
好吧,可能更好的方法是模拟 v-autocomplete 并将其替换为测试组件(只需控制传递给它的值并从中发出假事件)。您不是在开发 Vuetify,因此无需测试组件内部发生的情况。
在这种情况下,我想通过 vue-test-utils. I use 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>
我的问题是物品在哪里? <div class="v-menu"></div>
为什么我在 wrapper.html 中看不到。我不使用 shallowMount,我使用 mount。因此,我无法编写涵盖的测试用例。
如何模拟打开的菜单并呈现所有项目并提供一些断言?
我错过了什么?
版本
"@nuxtjs/vuetify": "1.12.1",
"@vue/test-utils": "1.0.0-beta.29",
"vue-jest": "3.0.7"
好吧,可能更好的方法是模拟 v-autocomplete 并将其替换为测试组件(只需控制传递给它的值并从中发出假事件)。您不是在开发 Vuetify,因此无需测试组件内部发生的情况。