test-utils icon indicating copy to clipboard operation
test-utils copied to clipboard

Better types for `wrapper.vm` when the component is closed

Open cexbrayat opened this issue 4 years ago • 18 comments

Since 2.0.0-rc.15, we are now exposing $.proxy as wrapper.vm to simplify testing of script setup component, allowing to test the following component:

<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>

with expect(wrapper.vm.count).toBe(0), even if count is not exposed by the component (see PR #931 ).

This works, but for TS (vue-tsc), wrapper.vm does not have a count property, because wrapper.vm is typed as ComponentPublicInstance.

Ideally, we would like wrapper.vm type to reflect that it is actually not the component public instance, but the properties exposed to the template. Is there a way to infer that?

To reproduce, remove "exclude": ["tests/expose.spec.ts"] from tsconfig.volar.json, and run yarn vue-tsc. It fails with:

tests/expose.spec.ts:38:23 - error TS2339: Property 'count' does not exist on type '{ $: ComponentInternalInstance; $data: {}; $props: Partial<{}> & Omit<Readonly<{} & {} & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch(source: string | Function, cb: Function, options?: WatchOptions<...> | undefined): WatchStopHandle; } & ... 4 more ... & ComponentC...'.

38     expect(wrapper.vm.count).toBe(1)

For readers interested in this issue, you can workaround it with an explicit cast: expect((wrapper.vm as unknown as { count: number }).count).toBe(1)

cexbrayat avatar Sep 24 '21 12:09 cexbrayat

@pikax do you have an idea how we could solve that?

cexbrayat avatar Sep 24 '21 12:09 cexbrayat

Currently that's not possible, I'm working on this PR https://github.com/vuejs/vue-next/pull/4465 to improve some types, I can have a look on how to also add that info there

pikax avatar Sep 24 '21 13:09 pikax

For anyone keeping up with this, @johnsoncodehk released [email protected] with a new option that improves the situation. See #1170

cexbrayat avatar Dec 22 '21 11:12 cexbrayat

The explicit cast solution stops working with the last Vue release (3.2.45).

felixzapata avatar Nov 14 '22 08:11 felixzapata

@felixzapata are you using VTU v2.2.2 ? There was a breaking change in Vue v3.2.45, but the latest VTU release should work around it.

cexbrayat avatar Nov 14 '22 08:11 cexbrayat

sorry, I forgot to mention that with VTU 2.2.2 also fails. I mean, I tested with Vue 3.2.45 and VTU 2.2.2

felixzapata avatar Nov 14 '22 08:11 felixzapata

@felixzapata In that case, can you open an issue with a small repro using https://stackblitz.com/edit/vitest-dev-vitest-a961ap?file=package.json&initialPath=vitest please? We'll take a look

cexbrayat avatar Nov 14 '22 08:11 cexbrayat

I will try because, besides the standard packages, we have some custom configurations and TypeScript typing rules.

I am reviewing the tests that are failing; all the ones we have using casting in order to spy methods.

felixzapata avatar Nov 14 '22 08:11 felixzapata

Is there a way to allow this mocking? Is similar to this one but the problem I think is because of mount and because I am late setting the mock. I think it should be during the mounting process.

felixzapata avatar Nov 30 '22 10:11 felixzapata

@felixzapata It is not possible, as this is not possible in vanilla JS. See https://github.com/vuejs/test-utils/issues/1798#issuecomment-1270223849 for more explanation.

cexbrayat avatar Nov 30 '22 10:11 cexbrayat

@felixzapata It is not possible, as this is not possible in vanilla JS. See #1798 (comment) for more explanation.

ok, maybe I have some thoughts of something similar I did in the past regarding the framework/library.

felixzapata avatar Nov 30 '22 11:11 felixzapata

This may have been improved for vue-tsc, but it still fails with Volar AFAIK.

matthew-dean avatar Feb 25 '23 20:02 matthew-dean

Is there any related issues for vue or volar that we could chime in on since this is an external issue?

pm0u avatar Nov 14 '23 16:11 pm0u

@pm0u I don't think there are such issues upstream (but maybe there are, I haven't checked recently). @pikax was working on a PR to improve the situation if I remember correctly, but I'm not sure of its status

cexbrayat avatar Nov 14 '23 16:11 cexbrayat

I saw that, however it looks like that has been untouched since 9/2021. The TS version referenced in the package json is a major version behind, so probably safe to say it is stale

pm0u avatar Nov 14 '23 16:11 pm0u

In component:

const slide = ref(0)

defineExpose({ slide })

In test:

it('has slide value 0', () => {
  expect(wrapper.vm.slide).toBe(0)
})

Not sure if proper solution, but solves the typing without any casts.

ulfgebhardt avatar Dec 13 '23 01:12 ulfgebhardt

@ulfgebhardt Thanks, but your solution is expected to work: this issue is for the cases where you don't want to expose the variable.

cexbrayat avatar Dec 13 '23 06:12 cexbrayat

This will require a few changes from https://github.com/vuejs/language-tools, since the SFC types are generated by it, I recommend creating a cross issue over there.

pikax avatar Dec 19 '23 10:12 pikax