diff --git a/examples/app-vitest-full/components/ComponentWithPluginProvidedValue.vue b/examples/app-vitest-full/components/ComponentWithPluginProvidedValue.vue new file mode 100644 index 000000000..b5d04e333 --- /dev/null +++ b/examples/app-vitest-full/components/ComponentWithPluginProvidedValue.vue @@ -0,0 +1,13 @@ + + + + {{ $pluginProvidedValues.value }} + + + {{ $pluginProvidedValues.func('value') }} + + + {{ $pluginProvidedValues.object.value }} + + + diff --git a/examples/app-vitest-full/plugins/provided-values.ts b/examples/app-vitest-full/plugins/provided-values.ts new file mode 100644 index 000000000..4610b074d --- /dev/null +++ b/examples/app-vitest-full/plugins/provided-values.ts @@ -0,0 +1,11 @@ +export default defineNuxtPlugin(() => { + return { + provide: { + pluginProvidedValues: { + value: 'pluginProvided.value', + func: (value: string) => `pluginProvided.func(${value})`, + object: { value: 'pluginProvided.object.value' }, + }, + }, + } +}) diff --git a/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts b/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts index d728866d8..cf1b01f22 100644 --- a/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts +++ b/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts @@ -26,6 +26,7 @@ import ComponentWithReservedProp from '~/components/ComponentWithReservedProp.vu import ComponentWithReservedState from '~/components/ComponentWithReservedState.vue' import ComponentWithImports from '~/components/ComponentWithImports.vue' import ComponentWithCssVar from '~/components/ComponentWithCssVar.vue' +import ComponentWithPluginProvidedValue from '~/components/ComponentWithPluginProvidedValue.vue' import GenericStateComponent from '~/components/GenericStateComponent.vue' import { BoundAttrs } from '#components' @@ -284,6 +285,13 @@ describe('mountSuspended', () => { expect(component.find('#s3').classes()).toHaveLength(0) }) + it('can mount components with use plugin provided value in template', async () => { + const component = await mountSuspended(ComponentWithPluginProvidedValue) + expect(component.find('#s1').text()).toBe('pluginProvided.value') + expect(component.find('#s2').text()).toBe('pluginProvided.func(value)') + expect(component.find('#s3').text()).toBe('pluginProvided.object.value') + }) + describe('Options API', () => { beforeEach(() => { vi.spyOn(console, 'error').mockImplementation((message) => { diff --git a/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts b/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts index 49b2ee324..1590bff14 100644 --- a/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts +++ b/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts @@ -13,6 +13,7 @@ import ExportDefaultComponent from '~/components/ExportDefaultComponent.vue' import ExportDefineComponent from '~/components/ExportDefineComponent.vue' import ComponentWithAttrs from '~/components/ComponentWithAttrs.vue' import ComponentWithCssVar from '~/components/ComponentWithCssVar.vue' +import ComponentWithPluginProvidedValue from '~/components/ComponentWithPluginProvidedValue.vue' import ExportDefaultWithRenderComponent from '~/components/ExportDefaultWithRenderComponent.vue' import ExportDefaultReturnsRenderComponent from '~/components/ExportDefaultReturnsRenderComponent.vue' import OptionsApiPage from '~/pages/other/options-api.vue' @@ -179,6 +180,13 @@ describe('renderSuspended', () => { expect(container.querySelector('#s3')?.classList).toHaveLength(0) }) + it('can render components with use plugin provided value in template', async () => { + const { container } = await renderSuspended(ComponentWithPluginProvidedValue) + expect(container.querySelector('#s1')?.textContent).toBe('pluginProvided.value') + expect(container.querySelector('#s2')?.textContent).toBe('pluginProvided.func(value)') + expect(container.querySelector('#s3')?.textContent).toBe('pluginProvided.object.value') + }) + describe('Options API', () => { beforeEach(() => { vi.spyOn(console, 'error').mockImplementation((message) => { diff --git a/src/runtime-utils/mount.ts b/src/runtime-utils/mount.ts index 085750448..b5496b8cd 100644 --- a/src/runtime-utils/mount.ts +++ b/src/runtime-utils/mount.ts @@ -1,7 +1,7 @@ import { mount } from '@vue/test-utils' import type { ComponentMountingOptions } from '@vue/test-utils' import { Suspense, h, isReadonly, nextTick, reactive, unref, getCurrentInstance, effectScope, isRef } from 'vue' -import type { ComponentInternalInstance, DefineComponent, SetupContext } from 'vue' +import type { App, ComponentInternalInstance, DefineComponent, SetupContext } from 'vue' import { defu } from 'defu' import type { RouteLocationRaw } from 'vue-router' @@ -63,7 +63,7 @@ export async function mountSuspended( cleanupFunction() } - const vueApp = tryUseNuxtApp()?.vueApp + const vueApp: App & Record = tryUseNuxtApp()?.vueApp // @ts-expect-error untyped global __unctx__ || globalThis.__unctx__.get('nuxt-app').tryUse().vueApp const { render, setup, data, computed, methods, ...componentRest } = component as DefineComponent, Record> @@ -261,7 +261,14 @@ export async function mountSuspended( attrs, global: { config: { - globalProperties: vueApp.config.globalProperties, + globalProperties: { + ...vueApp.config.globalProperties, + // make all properties/keys enumerable. + ...Object.fromEntries( + Object.getOwnPropertyNames(vueApp.config.globalProperties) + .map(key => [key, vueApp.config.globalProperties[key]]), + ), + }, }, directives: vueApp._context.directives, provide: vueApp._context.provides, diff --git a/src/runtime-utils/render.ts b/src/runtime-utils/render.ts index 116d8686f..4d315f375 100644 --- a/src/runtime-utils/render.ts +++ b/src/runtime-utils/render.ts @@ -1,5 +1,5 @@ import { Suspense, effectScope, h, nextTick, isReadonly, reactive, unref, defineComponent, getCurrentInstance } from 'vue' -import type { ComponentInternalInstance, DefineComponent, SetupContext } from 'vue' +import type { App, ComponentInternalInstance, DefineComponent, SetupContext } from 'vue' import type { RenderOptions as TestingLibraryRenderOptions } from '@testing-library/vue' import { defu } from 'defu' import type { RouteLocationRaw } from 'vue-router' @@ -59,7 +59,7 @@ export async function renderSuspended(component: T, options?: RenderOptions & Record = tryUseNuxtApp()?.vueApp // @ts-expect-error untyped global __unctx__ || globalThis.__unctx__.get('nuxt-app').tryUse().vueApp const { render, setup, data, computed, methods, ...componentRest } = component as DefineComponent, Record> @@ -251,7 +251,14 @@ export async function renderSuspended(component: T, options?: RenderOptions [key, vueApp.config.globalProperties[key]]), + ), + }, }, directives: vueApp._context.directives, provide: vueApp._context.provides,