Skip to content

Using slots with predefined components #191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
miljan-aleksic opened this issue Nov 20, 2017 · 10 comments
Closed

Using slots with predefined components #191

miljan-aleksic opened this issue Nov 20, 2017 · 10 comments

Comments

@miljan-aleksic
Copy link
Contributor

Hi, thank you for the amazing work, it looks very promising.

I noticed the docs don't cover some more complex components, eg, a data-table.

A data table is composed of the main component (table) and children (columns) that depend directly on the parent. For such situation using shallow on the Table would render the table partially, using it on columns would directly not work.

The solution seems to wrap it all up in a dummy component and mount that component instead. But that seems not ideal for the workflow vue-test-utils suggest and I am not sure how to proceed about this, thus opening this ticket.

@eddyerburgh
Copy link
Member

There was talk about including a depth option to shallow, so that you could choose how many children to render. This way you could add depth: 2 and have both your Table and Column components render.

Are you having problems using mount for this?

@miljan-aleksic
Copy link
Contributor Author

miljan-aleksic commented Nov 20, 2017

Thanks @eddyerburgh for your quick answer.

Not a problem, but I think I cannot use it if have children. Most likely I'm missing something here. This is the code I want to test, a simple snapshot test.

<vk-table middle divider :data="rows">
  <vk-table-column header="Website" cell="website" />
</vk-table>

Using mount or shallow I could render vk-table, but I don't see how can I set the vk-table-column as it children.

@LinusBorg
Copy link
Member

LinusBorg commented Nov 20, 2017

https://vue-test-utils.vuejs.org/en/api/options.html#slots

mount(VkTable, {
  propsData: {
    middle: true,
    divider: true,
    data: rows
  },
  slots: {
   default: `<vk-table-column header="Website" cell="website" />`
  }
})

(assuming vk-table was already registered as a component on VkTable

@miljan-aleksic
Copy link
Contributor Author

Thanks @LinusBorg, I tried this again and this time it just worked... the first time I did there was some issue that most likely confused me.

Thank you @eddyerburgh as well.

@miljan-aleksic
Copy link
Contributor Author

miljan-aleksic commented Nov 21, 2017

Sorry to bother again guys, but I reproduced now the issue that misled me initially.

As @LinusBorg and docs suggest the slots allows to render a component and pass it down to the mounted component. But, and at least in my tests, no matter what I set there, I got the same result in the $slots reference, a generic vnode.

At first, I thought it was because of a wrong component registration, but then I got the same results registering the component globally or locally.

This is my code so far:

import Vue from 'vue'
import { mount } from 'vue-test-utils'
import { createRenderer } from 'vue-server-renderer'

import VkTable from '~/components/table/table'
import VkTableColumn from '~/components/table/table-column'

Vue.component('VkTableColumn', VkTableColumn)

const wrapper = mount(VkTable, {
  slots: {
     default: '<vk-table-column head="Name" cell="name" />'
  }
})

Internally, VkTable will try to access VkTableColumn vnode through $slots.default and even though there is a component, it's definitely not VkTableColumn.

And as mentioned, this code would behave exactly the same:

const wrapper = mount(VkTable, {
  slots: {
     default: '<div />'
  }
})

What am I missing this time :)

@Austio
Copy link
Contributor

Austio commented Nov 22, 2017

@miljan-aleksic When you use mount, it uses a local vue instance to do all of it's magic, so in a localized test you will need to either pass the components to the wrapper or add the Column component to your Table's components.

  const wrapper = mount(Table, {
    slots: {
      default: '<Column cell="name"></Column>'
    },
    components: [Column]
  });

Side question, if that is what you are looking for and you end up closing this could you update the title to have something about slots in it so that others will more easily find?

@miljan-aleksic miljan-aleksic changed the title Missing more information about testing libraries Using slots with predefined components Nov 22, 2017
@miljan-aleksic
Copy link
Contributor Author

Hi @Austio, done but please feel free to change it as you think will best help the community.

I tried as suggested and is not working, then I tried passing a localVue registering the component to it. None does work, unfortunately. I tested with beta6.

I have few comments:

  1. A slot that cannot resolve a component should warn about it. Among others, it will make it more easy to detect if issues like this are because of a bad configuration

  2. Mixing mount options with Vue API is confusing. Is not clear what is being declared; a component instance or a wrapper. Separating concepts should be considered.

  3. If mount use a local Vue by default what is for the localVue option? The docs imply polluting global Vue should be avoided, but in fact that would not matter at all as mount will never use it.

Please take my comments as observations of a first time user who is not much experienced with testing tools.

@Austio
Copy link
Contributor

Austio commented Nov 22, 2017

Hmmm, I don't have any other thoughts, could create a jsfiddle or repo with minimal needed to reproduce?

@eddyerburgh
Copy link
Member

eddyerburgh commented Nov 22, 2017

A slot that cannot resolve a component should warn about it. Among others, it will make it more easy to detect if issues like this are because of a bad configuration

The issue here isn't bad configuration, it's to do with how we compile strings that are passed in the slots option.

At the moment only basic tags are supported as strings. Under the hood, we pass them to the vue-template-compiler compileToFunctions method.

I'll add an intend to implement tag and look into fixing this so that you can use a component as the slot like you intended 🙂

Mixing mount options with Vue API is confusing. Is not clear what is being declared; a component instance or a wrapper. Separating concepts should be considered.

I agree this is problematic. When we started this project, I took the decision to merge the two for aesthetic reasons:

const wrapper = mount(Component, {
  propsData: { text: 'hello' },
  mocks: { $route }
})
const wrapper = mount(Component, {
  mocks: { $route }
}, {
  propsData: { text: 'hello' },
})

In effect, everything that isn't a vue-test-utils mounting option is passed to the component when it's instantiated. We need a longer section in the docs explaining this, but I don't think we should separate out the options.

If mount use a local Vue by default what is for the localVue option? The docs imply polluting global Vue should be avoided, but in fact that would not matter at all as mount will never use it.

localVue is created by extending the global Vue class, so anything added to the global Vue will exist in a localVue. In other words, you might as well think of it as the global Vue class. It's a localVue internally because it means we don't accidentally pollute the global Vue class when we add the mounting options.

@miljan-aleksic
Copy link
Contributor Author

@eddyerburgh, thank you very much for your detailed explanation and intend to implement the missing feature. I deeply appreciate it ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants