Vue-test-utils: Test slot scope

Created on 6 Nov 2017  路  11Comments  路  Source: vuejs/vue-test-utils

We should add a way to test the scoped slots. For example, <slot v-for="..." :item="item" :index="index" /> 鉃★笍 We should be able to test the values of item and index in the slot.

feature request help wanted intend to implement

Most helpful comment

I try to resolve this issue.
I will send pull request within 3 days.

All 11 comments

ok so there's two problems:

  1. creating a proper scopeSlot Function
  2. adding the scoped slots content to the vm.

1. creating a proper scopeSlot Function

For normal slots, vue-test-utils uses vue-template-compiler, which still have some limitations.

My suggestion would be to use a real Vue instance instead to create the slot content:

function createSlotContent ({
  slotValue: string,
  slotName: string = 'default',
  scoped: Boolean = false
}) : Function |聽Array<VNode> {
  const slotParent = new Vue({
    components: {
      slotter: { template: `<div></div>`},
    }
    template: `<div><slotter ref="slotter">${slotValue}</slotter></div>`,
  })
  return slotParent.[scoped ? '$scopedSlots' : '$slots'][slotName]
}

2. adding the scoped slots content to the vm.

For normal slots, we simply do vm.$slots = slotContent, but that doesn't work for $scopedSlots because of this line in the vm._render function:

if (_parentVnode) {
      vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject
    }

This code will run everytime we (re-)render the vm, and since there isn't an actual parentVnode to read scoped slots from, we would have to mock it.

That would have to be tested extensively, because _parentVNode is accessed in multiple places for different things.

If that turns out to be too brittle, another way to go woudl be to change the way we do mount():

Right now we create a sandalone instance of the component under test. What about creating it as a child component in a parent instance, so it has a real parent?

// peudocode
const parent = new Vue({
  data: {
    propsData,
    $listeners,
    $attrs,
  },
  components: {
    testee: ComponentUnderTest,
  },
    template: `
<div>
  <testee 
    v-bind={...propsData, ...$attrs}
    v-on="$listeners"
    ref="testee"
  >
  ${stringBuiltFromAllslotValueStrings}
  </testee>
</div>
`
})

const vm = parent.$refs.testee

That would also solve the slots problem as Vue would take over the assignment. But this would also mean a big internal change, requiring adjustements in mutiple places....

I haven't checked this out in depth, just wanted to write down my thoughts in case they are helpful.

Adding a parent node would solve another problem we're having with $attrs. I think this could be a good solution.

I'll look into it.

Great! I'll also take a look between the holidays if I have an hour or two.

Is there anything I can do to help with this?

I try to resolve this issue.
I will send pull request within 3 days.

@38elements That would be great! We were thinking about creating a parent instance inside the create-instance method, like @LinusBorg suggested.

@eddyerburgh
Since I am unsure of whether my implementation method is correct,
I sent a pull request of incomplete code.
It is #507 .
I think it resolve to replace the renderSlot method.

This has been added in #507 and will be released in beta.15

@eddyerburgh is there a target release date for beta 15? I'm trying to finally start doing TDD and not being able to test scoped slots is blocking that. I tried building myself but no luck so far.

@smiller171 Yes, I'll release on Monday evening

Thanks for the quick response @eddyerburgh!

I managed to get my own copy running too, so I've got a stopgap.

Was this page helpful?
0 / 5 - 0 ratings