问题
I have two directives which are supposed to add data attributes to components for testing, however, only one of the directives actually gets added. The two components are Bootstrap-Vue's BFormInput and BButton.
I tried removing everything but one of the buttons and the directive is still not added i.e
<b-input-group class="sm-2 mb-2 mt-2">
<b-button
variant="primary"
@click="searchJobs"
class="rounded-0"
v-jobs-search-button-directive="{ id: 'search-button' }"
>
Search
</b-button>
</b-input-group>
wrapper.html() output is:
<b-input-group-stub tag="div" class="sm-2 mb-2 mt-2"><b-button-stub target="_self" event="click" routertag="a" variant="secondary" type="button" tag="button" class="rounded-0">
Search
</b-button-stub></b-input-group-stub>
However, it is added when instead of a button I leave in place the input form i.e.
<b-input-group class="sm-2 mb-2 mt-2">
<b-form-input
v-jobs-search-input-directive="{ id: 'input-keyword' }"
class="mr-2 rounded-0"
placeholder="Enter Search term..."
:value="this.searchConfig.Keyword"
@input="this.updateJobsSearchConfig"
/>
</b-input-group>
wrapper.html() output is:
<b-input-group-stub tag="div" class="sm-2 mb-2 mt-2"><b-form-input-stub value="" placeholder="Enter Search term..." type="text" class="mr-2 rounded-0" data-jobs-search-input-id="input-keyword"></b-form-input>
This is how I add the directives
<template>
<b-input-group class="sm-2 mb-2 mt-2">
<b-form-input
v-jobs-search-input-directive="{ id: 'input-keyword' }"
class="mr-2 rounded-0"
placeholder="Enter Search term..."
:value="this.searchConfig.Keyword"
@input="this.updateJobsSearchConfig"
/>
<b-button
variant="primary"
@click="searchJobs"
class="rounded-0"
v-jobs-search-button-directive="{ id: 'search-button' }"
>
Search
</b-button>
</b-input-group>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import JobService from '@/api-services/job.service'
import JobsSearchInputDirective from '@/directives/components/jobs/JobsSearchInputDirective'
import JobsSearchButtonDirective from '@/directives/components/jobs/JobsSearchButtonDirective'
export default {
name: 'jobs-search',
directives: { JobsSearchInputDirective, JobsSearchButtonDirective },
data () {
return {
jobs: [],
pages: 0
}
},
computed: {
...mapState({
pagedConfig: state => state.jobs.paged,
searchConfig: state => state.jobs.search
})
},
methods: {
// Methods go here
}
}
jobs-search-input-directive is
export default (el, binding) => {
if (process.env.NODE_ENV === 'test') {
Object.keys(binding.value).forEach(value => {
el.setAttribute(`data-jobs-search-input-${value}`, binding.value[value])
})
}
}
jobs-search-button-directive is
export default (el, binding) => {
if (process.env.NODE_ENV === 'test') {
Object.keys(binding.value).forEach(value => {
el.setAttribute(`data-jobs-search-button-${value}`, binding.value[value])
})
}
}
This is the test I run, mounting with shallowMount
it('should call jobsSearch method on search button click event', () => {
wrapper.find('[data-jobs-search-button-id="search-button"]').trigger('click')
expect(searchJobs).toHaveBeenCalled()
})
which comes back with
Error: [vue-test-utils]: find did not return [data-jobs-search-button-id="search-button"], cannot call trigger() on empty Wrapper
However wrapper.find('[data-jobs-search-input-id="input-keyword"]')
DOES find the input-form
The two directives are registered in the JobsSearch.vue
component and they definitely get rendered if I remove the process.env
part
I expect the attribute to be added to both components but it only gets added to the BFormInput when testing. Any help will be greatly appreciated.
回答1:
I believe that the problem occurs when...
- ... trying to use a directive...
- ... on a functional child component...
- ... with
shallowMount
.
b-button
is a functional component.
I've put together the demo below to illustrate the problem. It mounts the same component in 3 different ways and it only fails in the specific case outlined above.
MyComponent = {
template: `
<div>
<my-normal v-my-directive></my-normal>
<my-functional v-my-directive></my-functional>
</div>
`,
components: {
MyNormal: {
render: h => h('span', 'Normal')
},
MyFunctional: {
functional: true,
render: (h, context) => h('span', context.data, 'Functional')
}
},
directives: {
myDirective (el) {
el.setAttribute('name', 'Lisa')
}
}
}
const v = new Vue({
el: '#app',
components: {
MyComponent
}
})
document.getElementById('markup1').innerText = v.$el.innerHTML
const cmp1 = VueTestUtils.mount(MyComponent)
document.getElementById('markup2').innerText = cmp1.html()
const cmp2 = VueTestUtils.shallowMount(MyComponent)
document.getElementById('markup3').innerText = cmp2.html()
#markup1, #markup2, #markup3 {
border: 1px solid #777;
margin: 10px;
padding: 10px;
}
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/vue-template-compiler@2.6.10/browser.js"></script>
<script src="https://unpkg.com/@vue/test-utils@1.0.0-beta.29/dist/vue-test-utils.iife.js"></script>
<div id="app">
<my-component></my-component>
</div>
<div id="markup1"></div>
<div id="markup2"></div>
<div id="markup3"></div>
I haven't really looked at the code for vue-test-utils
before but stepping through in the debugger makes me suspicious of this line:
https://github.com/vuejs/vue-test-utils/blob/9dc90a3fd4818ff70e270568a2294b1d8aa2c3af/packages/create-instance/create-component-stubs.js#L99
This is the render
function for the stubbed child component. It would appear that context.data.directives
does contain the correct directive but they aren't being passed on in the call to h
.
Contrast that with the render
function in my example component MyFunctional
, which passes on all of data
. That's required for directives to work with a functional component but when MyFunctional
gets replaced with a stub the new render
function seems to drop the directives
property.
The only workaround I've been able to come up with is to provide your own stub:
VueTestUtils.shallowMount(MyComponent, {
stubs: {
BButton: { render: h => h('div')}
}
})
By using a non-functional stub the directive works fine. Not sure how much value this would take away from the test though.
来源:https://stackoverflow.com/questions/57808229/use-more-than-one-directive-to-add-data-attributes-to-components