test-utils icon indicating copy to clipboard operation
test-utils copied to clipboard

Behavior of global.components is confusing

Open xanf opened this issue 4 years ago • 6 comments

I'm struggling to understand the reasoning, why global.components perform two things simultaneously:

  • registers global components (this is perfectly fine)
  • acts as global.stubs, but just for component we are mounting

Second one is provided by this piece of code inside mount:

  if (isObjectComponent(component)) {
    component.components = { ...component.components, ...global.components }
  }

where global.components effectively overwrite local components.

I find this behavior super-confusing:

  • now we have two approaches for stubbing components, and one of them (via global.components) will work only for components, who use Options API
  • relationship between global.stubs and global.components becomes hard-to-explain

For example this test looks super-confusing unless you know what is happening [source]:

 it('allows global stubs to be deactivated without warning', () => {
    const GlobalComponent = {
      template: '<div>Global</div>'
    }
    const spy = jest.spyOn(console, 'warn')
    const wrapper = mount(
      {
        template: '<div><global-component/></div>'
      },
      {
        global: {
          components: {
            GlobalComponent
          },
          stubs: { GlobalComponent: false }
        }
      }
    )

You're like: ...em... what? I'm unstubbing GlobalComponent by passing GlobalComponent: false, but why it is stubbed, we're not using shallowMount?

I see no added benefits in current behavior and would like to suggest dropping it, and matching global.components application API app.component behavior - it simply registers component as global one and nothing more

xanf avatar Aug 09 '21 04:08 xanf

@lmiller1990 considering I saw some recent activity from you would be happy to hear your thoughts

xanf avatar Aug 10 '21 02:08 xanf

It like the change was made here: https://github.com/vuejs/vue-test-utils-next/pull/504 to support a use-case in the posva/vue-router-mock library.

I can see why this would be confusing. Have you encountered this use case in your usual development workflow? I have not really found myself using global.stubs and global.components in the same test, I'm guessing this is not a very common edge case.

It looks like reverting it might have some negative impacts, at least on the vue-router-mock library.

lmiller1990 avatar Aug 12 '21 13:08 lmiller1990

@lmiller1990 thank you for giving context! I will take a look and come back today with proposal

xanf avatar Aug 12 '21 14:08 xanf

@lmiller1990 I've checked that #504 is actually not the issue where problematic code was added Original source of the problematic code is https://github.com/vuejs/vue-test-utils-next/pull/116 and after analyzing discussion there I believe it was based at least on assumptions which are not in the play anymore

Regarding "if we meet this in the wild" - I've discovered this while preparing codemod, which "automatically" unpacked non-existent anymore parentComponent to relevant global properties. I still vote for having as simple mental model as possible.

xanf avatar Aug 29 '21 19:08 xanf

This behavior was very confusing me as well and the main challenge for me was that the globals are registered using app.component() and/or app.use() in Vue. However, this library uses an alternate array syntax for global registration.

Luckily, I was using a custom-built component library so I was able to create an "index" that I could import to this library. I then used a for-loop to register the components in my main.js file.

It seems that this library handles global registration in a completely different way from Vue itself which is very confusing.

vpillinger avatar Nov 10 '21 00:11 vpillinger

@vpillinger can you share a minimal example showing how Test Utils differs from Vue itself?

Also what do you mean by "array syntax"? You can supply components using an object: https://next.vue-test-utils.vuejs.org/api/#global

import Something from './Something.vue'

mount(Foo, {
  global: {
    components: {
      MyButton: Something,
      InlineComponent: {
        template: `<div>OK</div>`
      }
    }
  }
})

etc.

lmiller1990 avatar Nov 14 '21 22:11 lmiller1990