Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove symbol props from stubs #1086

Merged
merged 9 commits into from Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 26 additions & 2 deletions src/stubs.ts
Expand Up @@ -7,7 +7,8 @@ import {
defineComponent,
VNodeTypes,
ConcreteComponent,
ComponentPropsOptions
ComponentPropsOptions,
ComponentObjectPropsOptions
} from 'vue'
import { hyphenate } from './utils/vueShared'
import { matchName } from './utils/matchName'
Expand Down Expand Up @@ -42,6 +43,17 @@ const shouldNotStub = (type: ConcreteComponent) => doNotStubComponents.has(type)
export const addToDoNotStubComponents = (type: ConcreteComponent) =>
doNotStubComponents.add(type)

const stringifySymbols = (props: ComponentPropsOptions) => {
// props are always normalized to object syntax
const $props = props as unknown as ComponentObjectPropsOptions
return Object.keys($props).reduce((acc, key) => {
if (typeof $props[key] === 'symbol') {
return { ...acc, [key]: $props[key]?.toString() }
}
return { ...acc, [key]: $props[key] }
}, {})
}

export const createStub = ({
name,
propsDeclaration,
Expand All @@ -51,7 +63,19 @@ export const createStub = ({
const tag = name ? `${hyphenate(name)}-stub` : anonName

const render = (ctx: ComponentPublicInstance) => {
return h(tag, ctx.$props, renderStubDefaultSlot ? ctx.$slots : undefined)
// https://github.com/vuejs/vue-test-utils-next/issues/1076
// Passing a symbol as a static prop is not legal, since Vue will try to do
// something like `el.setAttribute('val', Symbol())` which is not valid and
// causes an error.
// Only a problem when shallow mounting. For this reason we iterate of the
// props that will be passed and stringify any that are symbols.
const propsWithoutSymbols = stringifySymbols(ctx.$props)

return h(
tag,
propsWithoutSymbols,
renderStubDefaultSlot ? ctx.$slots : undefined
)
}

return defineComponent({
Expand Down
13 changes: 13 additions & 0 deletions tests/components/PropWithSymbol.vue
@@ -0,0 +1,13 @@
<template>
<div>Symbol: {{ $props.sym }}</div>
</template>

<script lang="ts" setup>
interface Props {
sym?: Symbol
}

withDefaults(defineProps<Props>(), {
sym: () => Symbol()
})
</script>
54 changes: 53 additions & 1 deletion tests/props.spec.ts
@@ -1,5 +1,6 @@
import { mount } from '../src'
import { mount, shallowMount } from '../src'
import WithProps from './components/WithProps.vue'
import PropWithSymbol from './components/PropWithSymbol.vue'
import Hello from './components/Hello.vue'
import { defineComponent, h } from 'vue'

Expand Down Expand Up @@ -224,4 +225,55 @@ describe('props', () => {

expect(wrapper.text()).toEqual('hello')
})

describe('edge case with symbol props and stubs', () => {
it('works with Symbol as default', () => {
const Comp = defineComponent({
template: `<div>Symbol: {{ sym }}</div>`,
props: {
sym: {
type: Symbol,
default: () => Symbol()
}
}
})

const wrapper = shallowMount(Comp)

expect(wrapper.html()).toBe('<div>Symbol: Symbol()</div>')
})

it('works with symbol as array syntax', () => {
const Comp = defineComponent({
name: 'Comp',
template: `<div>Symbol: {{ sym }}</div>`,
props: ['sym']
})

const wrapper = shallowMount({
render() {
return h(Comp, { sym: Symbol() })
}
})

expect(wrapper.html()).toBe('<comp-stub sym="Symbol()"></comp-stub>')
})

it('works with symbol as default from SFC', () => {
const App = defineComponent({
template: `<PropWithSymbol :sym="sym" />`,
components: { PropWithSymbol },
data() {
return {
sym: Symbol()
}
}
})
const wrapper = shallowMount(App)

expect(wrapper.html()).toBe(
'<prop-with-symbol-stub sym="Symbol()"></prop-with-symbol-stub>'
)
})
})
})