Skip to content

Commit

Permalink
fix(runtime-utils): handle reserved props (#1025)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe authored Dec 3, 2024
1 parent 3225562 commit 302053c
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup lang="ts">
const props = defineProps<{
error?: string
}>()
</script>

<template>
<span>{{ props.error }}</span>
</template>
16 changes: 16 additions & 0 deletions examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -123,6 +124,21 @@ describe('mountSuspended', () => {
expect(component.html()).toMatchInlineSnapshot(`"<div data-directive="true"></div>"`)
})

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) => {
Expand Down
21 changes: 15 additions & 6 deletions src/runtime-utils/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,7 @@ export async function mountSuspended<T>(
const setProps = reactive<Record<string, unknown>>({})

let passedProps: Record<string, unknown>
const wrappedSetup = async (
props: Record<string, unknown>,
setupContext: SetupContext,
) => {
const wrappedSetup = async (props: Record<string, unknown>, setupContext: SetupContext) => {
passedProps = props
if (setup) {
const result = await setup(props, setupContext)
Expand Down Expand Up @@ -131,12 +128,16 @@ export async function mountSuspended<T>(
}
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<string, unknown>)
}
}
const propsContext = 'props' in renderContext ? renderContext.props as Record<string, unknown> : 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) {
Expand Down Expand Up @@ -196,3 +197,11 @@ const defuReplaceArray = createDefu((obj, key, value) => {
return true
}
})

function cloneProps(props: Record<string, unknown>) {
const newProps = reactive<Record<string, unknown>>({})
for (const key in props) {
newProps[key] = props[key]
}
return newProps
}
21 changes: 15 additions & 6 deletions src/runtime-utils/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ type SetupState = Record<string, any>
* @param component the component to be tested
* @param options optional options to set up your component
*/
export async function renderSuspended<T>(
component: T,
options?: RenderOptions<T>,
) {
export async function renderSuspended<T>(component: T, options?: RenderOptions<T>) {
const {
props = {},
attrs = {},
Expand Down Expand Up @@ -148,12 +145,16 @@ export async function renderSuspended<T>(
}
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<string, unknown>)
}
}
const propsContext = 'props' in renderContext ? renderContext.props as Record<string, unknown> : 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) {
Expand Down Expand Up @@ -203,3 +204,11 @@ declare global {
interface AugmentedVueInstance {
setupState?: SetupState
}

function cloneProps(props: Record<string, unknown>) {
const newProps = reactive<Record<string, unknown>>({})
for (const key in props) {
newProps[key] = props[key]
}
return newProps
}

0 comments on commit 302053c

Please sign in to comment.