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

fix: Stub instance of the same component #1979

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 7 additions & 8 deletions src/mount.ts
Expand Up @@ -45,7 +45,7 @@ import { attachEmitListener } from './emit'
import { createVNodeTransformer } from './vnodeTransformers/util'
import {
createStubComponentsTransformer,
addToDoNotStubComponents
CreateStubComponentsTransformerConfig
} from './vnodeTransformers/stubComponentsTransformer'
import { createStubDirectivesTransformer } from './vnodeTransformers/stubDirectivesTransformer'
import {
Expand Down Expand Up @@ -315,6 +315,9 @@ export function mount(
let component: ConcreteComponent
const instanceOptions = getInstanceOptions(options ?? {})

const rootComponents: CreateStubComponentsTransformerConfig['rootComponents'] =
{}

if (
isFunctionalComponent(originalComponent) ||
isLegacyFunctionalComponent(originalComponent)
Expand All @@ -335,14 +338,14 @@ export function mount(
h(originalComponent, { ...props, ...attrs }, slots),
...instanceOptions
})
addToDoNotStubComponents(originalComponent)
rootComponents.legacy = originalComponent
} else if (isObjectComponent(originalComponent)) {
component = { ...originalComponent, ...instanceOptions }
} else {
component = originalComponent
}

addToDoNotStubComponents(component)
rootComponents.component = component
// We've just replaced our component with its copy
// Let's register it as a stub so user can find it
registerStub({ source: originalComponent, stub: component })
Expand Down Expand Up @@ -464,11 +467,6 @@ export function mount(

// create the app
const app = createApp(Parent)
// the Parent type must not be stubbed
// but we can't add it directly, as createApp creates a copy
// and store it in app._component (since v3.2.32)
// So we store this one instead
addToDoNotStubComponents(app._component)

// add tracking for emitted events
// this must be done after `createApp`: https://github.com/vuejs/test-utils/issues/436
Expand Down Expand Up @@ -576,6 +574,7 @@ export function mount(
createVNodeTransformer({
transformers: [
createStubComponentsTransformer({
rootComponents,
stubs: getComponentsFromStubs(global.stubs),
shallow: options?.shallow,
renderStubDefaultSlot: global.renderStubDefaultSlot
Expand Down
25 changes: 16 additions & 9 deletions src/vnodeTransformers/stubComponentsTransformer.ts
Expand Up @@ -36,11 +36,6 @@ interface StubOptions {
renderStubDefaultSlot?: boolean
}

const doNotStubComponents: WeakSet<ConcreteComponent> = new WeakSet()
const shouldNotStub = (type: ConcreteComponent) => doNotStubComponents.has(type)
export const addToDoNotStubComponents = (type: ConcreteComponent) =>
doNotStubComponents.add(type)

const normalizeStubProps = (props: ComponentPropsOptions) => {
// props are always normalized to object syntax
const $props = props as unknown as ComponentObjectPropsOptions
Expand Down Expand Up @@ -102,13 +97,18 @@ const resolveComponentStubByName = (
}
}

interface CreateStubComponentsTransformerConfig {
export interface CreateStubComponentsTransformerConfig {
rootComponents: {
legacy?: Component
component?: Component
}
freakzlike marked this conversation as resolved.
Show resolved Hide resolved
stubs?: Record<string, Component | boolean>
shallow?: boolean
renderStubDefaultSlot: boolean
}

export function createStubComponentsTransformer({
rootComponents,
stubs = {},
shallow = false,
renderStubDefaultSlot = false
Expand Down Expand Up @@ -162,7 +162,14 @@ export function createStubComponentsTransformer({
})
}

if (shouldNotStub(type)) {
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.legacy && rootComponents.legacy === type)
) {
return type
}

Expand Down Expand Up @@ -218,7 +225,7 @@ export function createStubComponentsTransformer({
// Set name when using shallow without stub
const stubName = name || registeredName || componentName

const newStub =
return (
config.plugins.createStubs?.({
name: stubName,
component: type
Expand All @@ -228,7 +235,7 @@ export function createStubComponentsTransformer({
type,
renderStubDefaultSlot
})
return newStub
)
}

return type
Expand Down
14 changes: 14 additions & 0 deletions tests/components/RecursiveComponent.vue
@@ -0,0 +1,14 @@
<template>
<div>
<Hello />
<RecursiveComponent v-if="first" />
</div>
</template>

<script setup lang="ts">
import Hello from './Hello.vue'

defineProps<{
first?: boolean
}>()
</script>
16 changes: 16 additions & 0 deletions tests/shallowMount.spec.ts
Expand Up @@ -4,6 +4,7 @@ import { mount, shallowMount, VueWrapper } from '../src'
import ComponentWithChildren from './components/ComponentWithChildren.vue'
import ScriptSetupWithChildren from './components/ScriptSetupWithChildren.vue'
import DynamicComponentWithComputedProperty from './components/DynamicComponentWithComputedProperty.vue'
import RecursiveComponent from './components/RecursiveComponent.vue'

describe('shallowMount', () => {
it('renders props for stubbed component in a snapshot', () => {
Expand Down Expand Up @@ -73,6 +74,21 @@ describe('shallowMount', () => {
)
})

it('stub instance of same component', () => {
const wrapper = mount(RecursiveComponent, {
shallow: true,
props: {
first: true
}
})
expect(wrapper.html()).toEqual(
'<div>\n' +
' <hello-stub></hello-stub>\n' +
' <recursive-component-stub first="false"></recursive-component-stub>\n' +
'</div>'
)
})

it('correctly renders slot content', () => {
const ComponentWithSlot = defineComponent({
template: '<div><slot></slot></div>'
Expand Down