Skip to content
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

docs: How to test <teleport> #418

Closed
lmiller1990 opened this issue Feb 24, 2021 · 12 comments · Fixed by #670
Closed

docs: How to test <teleport> #418

lmiller1990 opened this issue Feb 24, 2021 · 12 comments · Fixed by #670
Labels
documentation Improvements or additions to documentation

Comments

@lmiller1990
Copy link
Member

Lots of good info here: #183

We should doc how to test regarding <teleport>.

@lmiller1990 lmiller1990 added the documentation Improvements or additions to documentation label Feb 24, 2021
@nandi95
Copy link
Contributor

nandi95 commented Mar 9, 2021

Things that might needs to be mentioned

  • You may find elements by various document.querySelector (you might extract this for clean code e.g.: getModal()?.html()) or perhaps by refs on the vm (less ideal)
  • You may need to call wrapper.unmount() after the tests
  • You may need to have an afterEach hook to do document.body.innerHtml = ''

@lmiller1990
Copy link
Member Author

Is anyone interested in making this PR? It would be greatly appreciated.

@snoozbuster
Copy link
Contributor

I found a case where we may actually want to update VTU. In particular - I have a component which renders:

  • a button
  • a modal (via teleport)
  • an alert (via teleport)

I would like to test the button by itself, without worrying about the teleport (I intend to test them in a more integrated test setup, whereas in these tests I am only concerned with the button). However, if I try to shallowMount this component, I get a bunch of exciting errors from Vue:

  console.warn node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:40
    [Vue warn]: Failed to locate Teleport target with selector "#alerts". Note the target element must exist before the component is mounted - i.e. the target cannot be rendered by the component itself, and ideally should be outside of the entire Vue component tree. 
      at <StatusIndicator loading=false retry=fn > 
      at <RequestLicenseButton applicationId="id" applicationName="name" ref="VTU_COMPONENT" > 
      at <VTUROOT>

  console.warn node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:40
    [Vue warn]: Invalid Teleport target on mount: null (object) 
      at <StatusIndicator loading=false retry=fn > 
      at <RequestLicenseButton applicationId="id" applicationName="name" ref="VTU_COMPONENT" > 
      at <VTUROOT>

  console.warn node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:40
    [Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next 
      at <StatusIndicator loading=true retry=fn errors=undefined > 
      at <RequestLicenseButton applicationId="id" applicationName="name" ref="VTU_COMPONENT" > 
      at <VTUROOT>

Ultimately, the tests fail with the following error:

TypeError: Cannot read property 'parentNode' of null

      at parentNode (node_modules/@vue/runtime-dom/dist/runtime-dom.cjs.js:33:30)
      at patchBlockChildren (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4000:19)
      at Object.process (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4981:17)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3719:26)
      at patchBlockChildren (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4004:13)
      at processFragment (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4059:17)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3709:17)
      at componentEffect (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4274:17)
      at Object.reactiveEffect [as update] (node_modules/@vue/reactivity/dist/reactivity.cjs.js:46:24)
      at updateComponent (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4157:26)
      at processComponent (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4092:13)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3716:21)
      at componentEffect (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4274:17)
      at reactiveEffect (node_modules/@vue/reactivity/dist/reactivity.cjs.js:46:24)
      at callWithErrorHandling (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:156:36)
      at flushJobs (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:382:17)

Stubbing <Teleport> has no discernible effect. It would be nice if VTU was able to stub <Teleport> (perhaps by merely forcing its disabled prop to true) so that I could write tests which can verify the logic of the component separately (eg, is alert rendered or not) without verifying the actual render behavior of the component (which I would like to test separately, so as to be able to isolate issues-with-component-logic vs issues-with-teleports).

I can achieve this by creating an additional component so that the tree looks like

<ComponentWithButtonAndTeleport>
  <MyButton />
  <Teleport> <Modal /> </Teleport>
  <Teleport> <Alert /> </Teleport>
</ComponentWithButtonAndTeleport>

and then I can write the UTs for <MyButton /> easily. But if I was to do this, there would be very little logic left in MyButton to actually test - I would have to test the entirety of ComponentWithButtonAndTeleport without UTs, even the logic which UTs are good for. Is this something that we can implement in VTU (should I open a new issue?), or will it be something that we solve via documentation?

@lmiller1990
Copy link
Member Author

lmiller1990 commented Mar 11, 2021

What happens if stub <Teleport>?

Can you post the exact component and test you want to write? I think it's probably good to have an solid example so we can prototype solutions.

If it can be solved with some simple boilerplate, that's probably ideal, but we should make sure it can solve all situations and is very well documented and understood. If it turns out there are some really difficult/impractical scenarios, we can add something.

@snoozbuster
Copy link
Contributor

snoozbuster commented Mar 11, 2021

That was with stubs: { Teleport: true } in the options - stubs: { Teleport: { template: '<slot />' } } has the same effect. (I thought I remembered a special case in stubs.ts which forced Teleport to be unstubbed, but I looked again only see special cases for Transition.)

It looks like Teleport doesn't have a name in vue-next - so I imagine this is the same issue as with Suspense and would require an upstream fix, no?

I was able to work around my immediate issue (testing non-Teleport logic in the same component) with a v-if on the Teleports... and I am wondering if there are best practices for Teleport which could be leveraged to sidestep the need to stub it. (for example - instead of <Teleport to="#alerts"> <Alert /> </Teleport>, maybe the best practice is actually putting the Teleport inside the Alert component?)

@cexbrayat
Copy link
Member

@snoozbuster Yeah you're probably right about the lack of name in vue-next. This can be an easy PR if you want to make a contribution to Vue 3 😉

@snoozbuster
Copy link
Contributor

I'm not actually sure that it will fix it. I noticed these lines in stubs.ts which return just a ['stub'] if there's no name - presumably Teleport would fall into this case, right? The Teleport implementation doesn't even have a render() like a normal component - I am wondering if Vue processes a teleport before VTU has a chance to stub it or something like that.

@lmiller1990
Copy link
Member Author

Possible... maybe time to do a bit of a deep dive into exactly how Teleport and stubs work to figure this one out.

@Leravig
Copy link

Leravig commented Jun 11, 2021

Are there any news about it? It would be nice to have a proper way to test components using teleport.

@lmiller1990
Copy link
Member Author

I will write something this week.

Any particular scenarios you'd like to see documented? I see several:

  1. I have a component with a <teleport>. How do I create an element to teleport to?
  2. I want to make assertions against the child content of a <teleport> (that were teleported away)

Eg:

<template>
  <teleport to="#farAway">
    <some-component :msg="dynamic" />
  </teleport>
</template>

Use case: you want to assert the dynamic msg was rendered. Is this the kind of guidance you are looking for? Are there any other examples you'd like to see included? If you can provide an actual scenario (ideally with code) I could use that as an example, too.

@lmiller1990
Copy link
Member Author

lmiller1990 commented Jun 17, 2021

You can follow my experiments and exploration here: #670

I'd like to use apply the VTU wrapper API to the teleported components and elements, too. Currently that isn't working, but I don't think it'll be too difficult to implement.

@lmiller1990
Copy link
Member Author

lmiller1990 commented Jun 23, 2021

Done: https://next.vue-test-utils.vuejs.org/guide/advanced/teleport.html. Pretty neat solution (imo).

I think this probably fine for now; we can consider more helpers/utils if this doesn't suffice.

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

Successfully merging a pull request may close this issue.

5 participants