From 5fe65ecc8f6c100466ae52c0defd9b0b9359f471 Mon Sep 17 00:00:00 2001 From: Romain Hamel Date: Sat, 1 Feb 2025 14:02:05 +0100 Subject: [PATCH] fix(runtime-utils): define `$attrs` in component render context (#1108) --- .../components/ComponentWithAttrs.vue | 9 +++++++++ .../tests/nuxt/mount-suspended.spec.ts | 6 ++++++ .../tests/nuxt/render-suspended.spec.ts | 8 ++++++++ src/runtime-utils/mount.ts | 1 + src/runtime-utils/render.ts | 12 +++++++++--- 5 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 examples/app-vitest-full/components/ComponentWithAttrs.vue diff --git a/examples/app-vitest-full/components/ComponentWithAttrs.vue b/examples/app-vitest-full/components/ComponentWithAttrs.vue new file mode 100644 index 000000000..20ec991d9 --- /dev/null +++ b/examples/app-vitest-full/components/ComponentWithAttrs.vue @@ -0,0 +1,9 @@ + + + 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 6604b1a3a..80417c415 100644 --- a/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts +++ b/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts @@ -14,6 +14,7 @@ import ExportDefineComponent from '~/components/ExportDefineComponent.vue' import ExportDefaultWithRenderComponent from '~/components/ExportDefaultWithRenderComponent.vue' import ExportDefaultReturnsRenderComponent from '~/components/ExportDefaultReturnsRenderComponent.vue' import OptionsApiPage from '~/pages/other/options-api.vue' +import ComponentWithAttrs from '~/components/ComponentWithAttrs.vue' import ComponentWithReservedProp from '~/components/ComponentWithReservedProp.vue' import ComponentWithReservedState from '~/components/ComponentWithReservedState.vue' import ComponentWithImports from '~/components/ComponentWithImports.vue' @@ -159,6 +160,11 @@ describe('mountSuspended', () => { expect(span.text()).toBe('false') }) + it('should define $attrs', async () => { + const component = await mountSuspended(ComponentWithAttrs, { attrs: { foo: 'bar' } }) + expect(component.find('[foo="bar"]').exists()).toBe(true) + }) + 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 58f75ecd1..3e1857b98 100644 --- a/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts +++ b/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts @@ -11,6 +11,7 @@ import DirectiveComponent from '~/components/DirectiveComponent.vue' import ExportDefaultComponent from '~/components/ExportDefaultComponent.vue' import ExportDefineComponent from '~/components/ExportDefineComponent.vue' +import ComponentWithAttrs from '~/components/ComponentWithAttrs.vue' import ExportDefaultWithRenderComponent from '~/components/ExportDefaultWithRenderComponent.vue' import ExportDefaultReturnsRenderComponent from '~/components/ExportDefaultReturnsRenderComponent.vue' import OptionsApiPage from '~/pages/other/options-api.vue' @@ -126,6 +127,13 @@ describe('renderSuspended', () => { `) }) + it('should define $attrs', async () => { + const { html } = await renderSuspended(ComponentWithAttrs, { attrs: { foo: 'bar' } }) + expect(html()).toMatchInlineSnapshot(` + "
" + `) + }) + 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 a2dd2e4e4..28d7d1923 100644 --- a/src/runtime-utils/mount.ts +++ b/src/runtime-utils/mount.ts @@ -182,6 +182,7 @@ export async function mountSuspended( _options, { slots, + attrs, global: { config: { globalProperties: vueApp.config.globalProperties, diff --git a/src/runtime-utils/render.ts b/src/runtime-utils/render.ts index d8cadb3da..184a3d27c 100644 --- a/src/runtime-utils/render.ts +++ b/src/runtime-utils/render.ts @@ -1,4 +1,4 @@ -import { Suspense, effectScope, h, nextTick, isReadonly, reactive, unref } from 'vue' +import { Suspense, effectScope, h, nextTick, isReadonly, reactive, unref, defineComponent } from 'vue' import type { DefineComponent, SetupContext } from 'vue' import type { RenderOptions as TestingLibraryRenderOptions } from '@testing-library/vue' import { defu } from 'defu' @@ -85,6 +85,12 @@ export async function renderSuspended(component: T, options?: RenderOptions & { setupState: SetupState }>((resolve) => { const utils = renderFromTestingLibrary( { @@ -108,8 +114,7 @@ export async function renderSuspended(component: T, options?: RenderOptions(component: T, options?: RenderOptions