diff --git a/src/vnodeTransformers/stubComponentsTransformer.ts b/src/vnodeTransformers/stubComponentsTransformer.ts index 78aeae2943..24c855c8a7 100644 --- a/src/vnodeTransformers/stubComponentsTransformer.ts +++ b/src/vnodeTransformers/stubComponentsTransformer.ts @@ -11,8 +11,8 @@ import { ConcreteComponent, ComponentPropsOptions, ComponentObjectPropsOptions, - DefineComponent, - Component + Component, + ComponentOptions } from 'vue' import { hyphenate } from '../utils/vueShared' import { matchName } from '../utils/matchName' @@ -28,6 +28,7 @@ import { registerStub } from '../stubs' export type CustomCreateStub = (params: { name: string component: ConcreteComponent + registerStub: (config: { source: Component; stub: Component }) => void }) => ConcreteComponent interface StubOptions { @@ -54,7 +55,7 @@ export const createStub = ({ name, type, renderStubDefaultSlot -}: StubOptions): DefineComponent => { +}: StubOptions) => { const anonName = 'anonymous-stub' const tag = name ? `${hyphenate(name)}-stub` : anonName @@ -62,7 +63,7 @@ export const createStub = ({ ? unwrapLegacyVueExtendComponent(type) || {} : {} - return defineComponent({ + const stub = defineComponent({ name: name || anonName, props: (componentOptions as ConcreteComponent).props || {}, // fix #1550 - respect old-style v-model for shallow mounted components with @vue/compat @@ -84,6 +85,18 @@ export const createStub = ({ } } }) + + const { __asyncLoader: asyncLoader } = type as ComponentOptions + if (asyncLoader) { + asyncLoader().then(() => { + registerStub({ + source: (type as ComponentOptions).__asyncResolved, + stub + }) + }) + } + + return stub } const resolveComponentStubByName = ( @@ -230,7 +243,8 @@ export function createStubComponentsTransformer({ return ( config.plugins.createStubs?.({ name: stubName, - component: type + component: type, + registerStub }) ?? createStub({ name: stubName, diff --git a/tests/features/async-components.spec.ts b/tests/features/async-components.spec.ts index b1311e979a..b04a730d8a 100644 --- a/tests/features/async-components.spec.ts +++ b/tests/features/async-components.spec.ts @@ -1,6 +1,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' import { defineAsyncComponent, defineComponent, h, AppConfig } from 'vue' -import { mount, flushPromises } from '../../src' +import { mount, shallowMount, flushPromises } from '../../src' +import Hello from '../components/Hello.vue' const config: Partial = { errorHandler: (error: unknown) => { @@ -108,4 +109,38 @@ describe('defineAsyncComponent', () => { await vi.dynamicImportSettled() expect(wrapper.html()).toContain('Hello world') }) + + it('finds Async Component by async definition when using shallow mount', async () => { + const AsyncHello = defineAsyncComponent( + () => import('../components/Hello.vue') + ) + + const Comp = defineComponent({ + render() { + return h(AsyncHello) + } + }) + + const wrapper = shallowMount(Comp) + await flushPromises() + await vi.dynamicImportSettled() + expect(wrapper.findComponent(AsyncHello).exists()).toBe(true) + }) + + it('finds Async Component by definition when using shallow mount', async () => { + const AsyncHello = defineAsyncComponent( + () => import('../components/Hello.vue') + ) + + const Comp = defineComponent({ + render() { + return h(AsyncHello) + } + }) + + const wrapper = shallowMount(Comp) + await flushPromises() + await vi.dynamicImportSettled() + expect(wrapper.findComponent(Hello).exists()).toBe(true) + }) }) diff --git a/tests/features/plugins.spec.ts b/tests/features/plugins.spec.ts index 7824768ace..b817a21ecb 100644 --- a/tests/features/plugins.spec.ts +++ b/tests/features/plugins.spec.ts @@ -8,7 +8,7 @@ import { vi } from 'vitest' import { ComponentPublicInstance, h } from 'vue' - +import { registerStub } from '../../src/stubs' import { mount, config, VueWrapper } from '../../src' declare module '../../src/vueWrapper' { @@ -145,11 +145,13 @@ describe('createStubs', () => { expect(customCreateStub).toHaveBeenCalledTimes(2) expect(customCreateStub).toHaveBeenCalledWith({ name: 'child1', - component: Child1 + component: Child1, + registerStub }) expect(customCreateStub).toHaveBeenCalledWith({ name: 'child2', - component: Child2 + component: Child2, + registerStub }) }) @@ -173,7 +175,8 @@ describe('createStubs', () => { expect(customCreateStub).toHaveBeenCalledTimes(1) expect(customCreateStub).toHaveBeenCalledWith({ name: 'child2', - component: Child2 + component: Child2, + registerStub }) }) @@ -212,7 +215,8 @@ describe('createStubs', () => { expect(customCreateStub).toHaveBeenCalledTimes(1) expect(customCreateStub).toHaveBeenCalledWith({ name: 'child1', - component: Child1 + component: Child1, + registerStub }) }) }) diff --git a/tests/mountingOptions/global.stubs.spec.ts b/tests/mountingOptions/global.stubs.spec.ts index 2712cdb360..ab44577964 100644 --- a/tests/mountingOptions/global.stubs.spec.ts +++ b/tests/mountingOptions/global.stubs.spec.ts @@ -679,16 +679,16 @@ describe('mounting options: stubs', () => { }) describe('stub async component', () => { - const AsyncComponent = defineAsyncComponent(async () => ({ - name: 'AsyncComponent', - template: 'AsyncComponent' - })) - const AsyncComponentWithoutName = defineAsyncComponent(async () => ({ template: 'AsyncComponent' })) it('stubs async component with name', async () => { + const AsyncComponent = defineAsyncComponent(async () => ({ + name: 'AsyncComponent', + template: 'AsyncComponent' + })) + const TestComponent = defineComponent({ components: { MyComponent: AsyncComponent @@ -704,7 +704,6 @@ describe('mounting options: stubs', () => { } }) - // flushPromises required to resolve async component expect(wrapper.html()).not.toBe( '' ) @@ -716,6 +715,11 @@ describe('mounting options: stubs', () => { }) it('stubs async component with name by alias', () => { + const AsyncComponent = defineAsyncComponent(async () => ({ + name: 'AsyncComponent', + template: 'AsyncComponent' + })) + const TestComponent = defineComponent({ components: { MyComponent: AsyncComponent