Skip to content

Commit

Permalink
fix: stricter props types
Browse files Browse the repository at this point in the history
At the moment, `mount()` offers [strong typing][1] of props, but this
strong typing is lost when dealing with the `VueWrapper` through either
the `props()` or `setProps()` methods.

This change strengthens the typing of these methods to help raise
compile-time errors when trying to get or set incorrect props.

[1]: https://github.com/vuejs/test-utils/blob/11b34745e8e66fc747881dfb1ce94cef537c455e/src/types.ts#L44
  • Loading branch information
alecgibson authored and cexbrayat committed Oct 17, 2023
1 parent 9dc7d1e commit 1db42c8
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 10 deletions.
8 changes: 7 additions & 1 deletion src/mount.ts
Expand Up @@ -47,7 +47,13 @@ export function mount<
>(
originalComponent: T,
options?: ComponentMountingOptions<C>
): VueWrapper<ComponentExposed<C> & ComponentProps<C> & ComponentData<C>>
): VueWrapper<
ComponentProps<C> & ComponentData<C> & ComponentExposed<C>,
ComponentPublicInstance<
ComponentProps<C>,
ComponentData<C> & ComponentExposed<C>
>
>

// implementation
export function mount(
Expand Down
14 changes: 9 additions & 5 deletions src/vueWrapper.ts
Expand Up @@ -215,10 +215,14 @@ export class VueWrapper<
return this.componentVM
}

props(): { [key: string]: any }
props(selector: string): any
props(selector?: string): { [key: string]: any } | any {
const props = this.componentVM.$props as { [key: string]: any }
props(): T['$props']
props<Selector extends keyof T['$props']>(
selector: Selector
): T['$props'][Selector]
props<Selector extends keyof T['$props']>(
selector?: Selector
): T['$props'] | T['$props'][Selector] {
const props = this.componentVM.$props as T['$props']
return selector ? props[selector] : props
}

Expand All @@ -240,7 +244,7 @@ export class VueWrapper<
return nextTick()
}

setProps(props: Record<string, unknown>): Promise<void> {
setProps(props: T['$props']): Promise<void> {
// if this VM's parent is not the root or if setProps does not exist, error out
if (this.vm.$parent !== this.rootVM || !this.__setProps) {
throw Error('You can only use setProps on your mounted component')
Expand Down
27 changes: 26 additions & 1 deletion test-dts/wrapper.d-test.ts
Expand Up @@ -116,4 +116,29 @@ expectType<boolean>(domWrapper.classes('class'))

// props
expectType<{ [key: string]: any }>(wrapper.props())
expectType<any>(wrapper.props('prop'))

const ComponentWithProps = defineComponent({
props: {
foo: String,
bar: Number,
},
})

const propsWrapper = mount(ComponentWithProps);

propsWrapper.setProps({foo: 'abc'})
propsWrapper.setProps({foo: 'abc', bar: 123})
// @ts-expect-error :: should require string
propsWrapper.setProps({foo: 123})
// @ts-expect-error :: unknown prop
propsWrapper.setProps({badProp: true})

expectType<string | undefined>(propsWrapper.props().foo)
expectType<number | undefined>(propsWrapper.props().bar)
// @ts-expect-error :: unknown prop
propsWrapper.props().badProp;

expectType<string | undefined>(propsWrapper.props('foo'))
expectType<number | undefined>(propsWrapper.props('bar'))
// @ts-expect-error :: unknown prop
propsWrapper.props('badProp')
10 changes: 7 additions & 3 deletions tests/getComponent.spec.ts
@@ -1,5 +1,5 @@
import { describe, expect, it, vi } from 'vitest'
import { DefineComponent, defineComponent } from 'vue'
import { defineComponent } from 'vue'
import { mount, RouterLinkStub, shallowMount } from '../src'
import Issue425 from './components/Issue425.vue'

Expand Down Expand Up @@ -70,15 +70,19 @@ describe('getComponent', () => {
// https://github.com/vuejs/test-utils/issues/425
it('works with router-link and mount', () => {
const wrapper = mount(Issue425, options)
expect(wrapper.getComponent<DefineComponent>('.link').props('to')).toEqual({
expect(
wrapper.getComponent<typeof RouterLinkStub>('.link').props('to')
).toEqual({
name
})
})

// https://github.com/vuejs/test-utils/issues/425
it('works with router-link and shallowMount', () => {
const wrapper = shallowMount(Issue425, options)
expect(wrapper.getComponent<DefineComponent>('.link').props('to')).toEqual({
expect(
wrapper.getComponent<typeof RouterLinkStub>('.link').props('to')
).toEqual({
name
})
})
Expand Down
1 change: 1 addition & 0 deletions tests/props.spec.ts
Expand Up @@ -20,6 +20,7 @@ describe('props', () => {

it('returns undefined if props does not exist', () => {
const wrapper = mount(WithProps, { props: { msg: 'ABC' } })
// @ts-expect-error :: non-existent prop
expect(wrapper.props('foo')).toEqual(undefined)
})

Expand Down

0 comments on commit 1db42c8

Please sign in to comment.