diff --git a/src/createInstance.ts b/src/createInstance.ts index 468173313..bb79fe94c 100644 --- a/src/createInstance.ts +++ b/src/createInstance.ts @@ -307,6 +307,7 @@ export function createInstance( // stub out Transition and Transition Group by default. transformVNodeArgs( createVNodeTransformer({ + rootComponents, transformers: [ createStubComponentsTransformer({ rootComponents, diff --git a/src/vnodeTransformers/stubComponentsTransformer.ts b/src/vnodeTransformers/stubComponentsTransformer.ts index 24c855c8a..6a5ce6c6b 100644 --- a/src/vnodeTransformers/stubComponentsTransformer.ts +++ b/src/vnodeTransformers/stubComponentsTransformer.ts @@ -1,4 +1,9 @@ -import { isKeepAlive, isTeleport, VTUVNodeTypeTransformer } from './util' +import { + isKeepAlive, + isRootComponent, + isTeleport, + VTUVNodeTypeTransformer +} from './util' import { Transition, TransitionGroup, @@ -177,14 +182,8 @@ export function createStubComponentsTransformer({ }) } - if ( - // Don't stub VTU_ROOT component - !instance || - // Don't stub mounted component on root level - (rootComponents.component === type && !instance?.parent) || - // Don't stub component with compat wrapper - (rootComponents.functional && rootComponents.functional === type) - ) { + // Don't stub root components + if (isRootComponent(rootComponents, type, instance)) { return type } diff --git a/src/vnodeTransformers/util.ts b/src/vnodeTransformers/util.ts index c79bccd86..85e443738 100644 --- a/src/vnodeTransformers/util.ts +++ b/src/vnodeTransformers/util.ts @@ -1,6 +1,6 @@ import { isComponent } from '../utils' import { registerStub } from '../stubs' -import { ConcreteComponent, transformVNodeArgs } from 'vue' +import { Component, ConcreteComponent, transformVNodeArgs } from 'vue' type VNodeArgsTransformerFn = NonNullable< Parameters[0] @@ -23,9 +23,30 @@ export type VTUVNodeTypeTransformer = ( export const isTeleport = (type: any): boolean => type.__isTeleport export const isKeepAlive = (type: any): boolean => type.__isKeepAlive +export interface RootComponents { + // Component which has been passed to mount. For functional components it contains a wrapper + component?: Component + // If component is functional then contains the original component otherwise empty + functional?: Component +} +export const isRootComponent = ( + rootComponents: RootComponents, + type: VNodeTransformerInputComponentType, + instance: InstanceArgsType +): boolean => + !!( + !instance || + // Don't stub mounted component on root level + (rootComponents.component === type && !instance?.parent) || + // Don't stub component with compat wrapper + (rootComponents.functional && rootComponents.functional === type) + ) + export const createVNodeTransformer = ({ + rootComponents, transformers }: { + rootComponents: RootComponents transformers: VTUVNodeTypeTransformer[] }): VNodeArgsTransformerFn => { const transformationCache: WeakMap< @@ -40,8 +61,14 @@ export const createVNodeTransformer = ({ return [originalType, props, children, ...restVNodeArgs] } + const componentType: VNodeTransformerInputComponentType = originalType + const cachedTransformation = transformationCache.get(originalType) - if (cachedTransformation) { + if ( + cachedTransformation && + // Don't use cache for root component, as it could use stubbed recursive component + !isRootComponent(rootComponents, componentType, instance) + ) { // https://github.com/vuejs/test-utils/issues/1829 & https://github.com/vuejs/test-utils/issues/1888 // Teleport/KeepAlive should return child nodes as a function if (isTeleport(originalType) || isKeepAlive(originalType)) { @@ -50,8 +77,6 @@ export const createVNodeTransformer = ({ return [cachedTransformation, props, children, ...restVNodeArgs] } - const componentType: VNodeTransformerInputComponentType = originalType - const transformedType = transformers.reduce( (type, transformer) => transformer(type, instance), componentType diff --git a/tests/components/RecursiveComponent.vue b/tests/components/RecursiveComponent.vue index 63b34a344..a0a69d7a0 100644 --- a/tests/components/RecursiveComponent.vue +++ b/tests/components/RecursiveComponent.vue @@ -1,7 +1,15 @@ @@ -9,6 +17,7 @@ import Hello from './Hello.vue' defineProps<{ - first?: boolean + name: string + items?: string[] }>() diff --git a/tests/shallowMount.spec.ts b/tests/shallowMount.spec.ts index 21209f04e..2a2fba099 100644 --- a/tests/shallowMount.spec.ts +++ b/tests/shallowMount.spec.ts @@ -74,19 +74,29 @@ describe('shallowMount', () => { ) }) - it('stub instance of same component', () => { + it('stub instance of same component', async () => { const wrapper = mount(RecursiveComponent, { shallow: true, props: { - first: true + name: '1', + items: ['2'] } }) expect(wrapper.html()).toEqual( '
\n' + + '

1

\n' + ' \n' + - ' \n' + + ' \n' + '
' ) + + expect(wrapper.find('h2').text()).toBe('1') + + await wrapper.setProps({ + name: '3' + }) + + expect(wrapper.find('h2').text()).toBe('3') }) it('correctly renders slot content', () => {