From 8ae1ddd025f492e862124972b6ff59010f01a552 Mon Sep 17 00:00:00 2001 From: freakzlike Date: Thu, 18 May 2023 10:34:34 +0200 Subject: [PATCH] fix(stub): re-render of recursive component using wrong cached stub --- src/createInstance.ts | 1 + .../stubComponentsTransformer.ts | 17 +++++----- src/vnodeTransformers/util.ts | 33 ++++++++++++++++--- 3 files changed, 38 insertions(+), 13 deletions(-) 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