feat(runtime): add scoped option to cleanup components#1389
feat(runtime): add scoped option to cleanup components#1389
scoped option to cleanup components#1389Conversation
- Wrap component setup and Nuxt root setup in effect scopes - Add global cleanup mechanism to prevent state persistence between tests - Ensure watchers and reactive effects are properly disposed - Improves test isolation for mountSuspended components
commit: |
danielroe
left a comment
There was a problem hiding this comment.
The difference is that render only expects a single component to render, but you might mount more than one component in a test.
Additionally, some people mount a component and then have several assertions (or 'it' tests) about it.
I think it's better to require people to unmount components manually, which is also (afaik) what you have to do if you are using @vue/test-utils.
I think it's unintuitive that state is shared between tests and wouldn't expect the order of calling tests to influence the result. If people do multiple assertions without mounting the component again I think they're writing their tests wrong. |
|
A test library like this should be unopinionated about how people do their mounting of components. I think we should follow the example of Some code that would be broken: it('implicitly unmounts comp1, unexpectedly', async () => {
const comp1 = await mountSuspended(/* */)
const comp2 = await mountSuspended(/* */)
}) |
InvestigationJust checked in vue: https://github.com/MikeBellika/vue-test-mount-repro Just tried to run this test with my change: it('can mount twice', async () => {
useCounterMock.mockRestore()
const comp1 = await mountSuspended(GenericStateComponent)
console.log('comp1 mounted')
const comp2 = await mountSuspended(GenericStateComponent)
console.log('comp2 mounted')
expect(comp1.text()).toMatchInlineSnapshot('"Zero or negative count"')
expect(comp2.text()).toMatchInlineSnapshot('"Zero or negative count"')
})I also updated <script setup lang="ts">
const { isPositive } = useCounter()
const { data } = useLazyAsyncData(async () => {
return isPositive() ? 'positive' : 'zero-or-negative'
})
onUnmounted(() => {
// not called
console.log('unmounted')
})
onScopeDispose(() => {
console.log('disposed')
})
</script>
<template>
<div v-if="data === 'positive'">
Positive count
</div>
<div v-else>
Zero or negative count
</div>
</template>The output is: So the components aren't unmounted within the test but the scopes do seem to be disposed. I don't know what effect this will have. My opinionAs a user of nuxt test-utils the behaviour of different invocations of My mental model of Alternatively what about an |
|
I'd be very happy to support an option (maybe however, isolated is a bit misleading as there's a single |
|
What about I'm not too strong on Nuxt internals but I think there are two related issues that I have gotten a bit mixed up.
What do you think about:
I can do 1. but I wouldn't know where to start with 2. Regarding 1. the docs do mention that a global nuxt instance will be used but I think it could be made more clear:
Source: https://nuxt.com/docs/4.x/getting-started/testing#running-tests |
|
@danielroe I've updated the PR to add a |
scoped option to automatically cleanup components
scoped option to automatically cleanup componentsscoped option to cleanup components
🔗 Linked issue
resolves #517
resolves #1367
❓ Type of change
📚 Description
This PR adds a
scopedoption tomountSuspended. When this option is enabled, state isn't shared between components. This is achieved by using an effectScope and a "global" cleanup function. This is similar to what is done in https://github.com/nuxt/test-utils/blob/main/src/runtime-utils/render.tsThe watcher cleanup tests are adapted from #1367. The
useCountertests are adapted from https://github.com/MikeBellika/component-not-unmounted-test-utils/blob/main/test/unit/repro.test.ts