Use more than one directive to add data attributes to components

大憨熊 提交于 2019-12-11 04:58:54

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!