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: stricter props types #2137

Merged
merged 1 commit into from
Oct 17, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/mount.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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