diff --git a/examples/app-vitest-full/components/ComponentWithReservedProp.vue b/examples/app-vitest-full/components/ComponentWithReservedProp.vue new file mode 100644 index 000000000..de02fca8f --- /dev/null +++ b/examples/app-vitest-full/components/ComponentWithReservedProp.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 d9aa769be..cd047eaa2 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 ComponentWithReservedProp from '~/components/ComponentWithReservedProp.vue' import { BoundAttrs } from '#components' import DirectiveComponent from '~/components/DirectiveComponent.vue' @@ -123,6 +124,21 @@ describe('mountSuspended', () => { expect(component.html()).toMatchInlineSnapshot(`"
"`) }) + it('can handle reserved words in component props', async () => { + const comp = await mountSuspended(ComponentWithReservedProp, { + props: { + error: '404', + }, + }) + const span = comp.find('span') + expect(span.text()).toBe('404') + + await comp.setProps({ + error: '500', + }) + expect(span.text()).toBe('500') + }) + 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 919c860f4..0ce997e8b 100644 --- a/src/runtime-utils/mount.ts +++ b/src/runtime-utils/mount.ts @@ -66,10 +66,7 @@ export async function mountSuspended( const setProps = reactive>({}) let passedProps: Record - const wrappedSetup = async ( - props: Record, - setupContext: SetupContext, - ) => { + const wrappedSetup = async (props: Record, setupContext: SetupContext) => { passedProps = props if (setup) { const result = await setup(props, setupContext) @@ -131,12 +128,16 @@ export async function mountSuspended( } for (const key in setupState || {}) { renderContext[key] = isReadonly(setupState[key]) ? unref(setupState[key]) : setupState[key] + if (key === 'props') { + renderContext[key] = cloneProps(renderContext[key] as Record) + } } + const propsContext = 'props' in renderContext ? renderContext.props as Record : renderContext for (const key in props || {}) { - renderContext[key] = _ctx[key] + propsContext[key] = _ctx[key] } for (const key in passedProps || {}) { - renderContext[key] = passedProps[key] + propsContext[key] = passedProps[key] } if (methods && typeof methods === 'object') { for (const key in methods) { @@ -196,3 +197,11 @@ const defuReplaceArray = createDefu((obj, key, value) => { return true } }) + +function cloneProps(props: Record) { + const newProps = reactive>({}) + for (const key in props) { + newProps[key] = props[key] + } + return newProps +} diff --git a/src/runtime-utils/render.ts b/src/runtime-utils/render.ts index b9ec3a5af..2126cb8e1 100644 --- a/src/runtime-utils/render.ts +++ b/src/runtime-utils/render.ts @@ -47,10 +47,7 @@ type SetupState = Record * @param component the component to be tested * @param options optional options to set up your component */ -export async function renderSuspended( - component: T, - options?: RenderOptions, -) { +export async function renderSuspended(component: T, options?: RenderOptions) { const { props = {}, attrs = {}, @@ -148,12 +145,16 @@ export async function renderSuspended( } for (const key in setupState || {}) { renderContext[key] = isReadonly(setupState[key]) ? unref(setupState[key]) : setupState[key] + if (key === 'props') { + renderContext[key] = cloneProps(renderContext[key] as Record) + } } + const propsContext = 'props' in renderContext ? renderContext.props as Record : renderContext for (const key in props || {}) { - renderContext[key] = _ctx[key] + propsContext[key] = _ctx[key] } for (const key in passedProps || {}) { - renderContext[key] = passedProps[key] + propsContext[key] = passedProps[key] } if (methods && typeof methods === 'object') { for (const key in methods) { @@ -203,3 +204,11 @@ declare global { interface AugmentedVueInstance { setupState?: SetupState } + +function cloneProps(props: Record) { + const newProps = reactive>({}) + for (const key in props) { + newProps[key] = props[key] + } + return newProps +}