4

I am starting to use Vue3's SFC script setup syntax: https://vuejs.org/api/sfc-script-setup.html

When I try to write unit tests for these components the Intellisense complains that the functions declared within these components do not exist.

The error is on the wrapper.vm

Property 'functionName' does not exist on type

My question is am I missing a package or tool that can resolve this issue?

I can cast the wrapper.vm as any to resolve the issue but it is not ideal...

I have added more details below:

test component test component unit test with error

I can run the unit test and it will pass successfully, however if I try to build the project I will receive an error.

test success build error

Here are some of the related packages in my package.json

"@vue/vue3-jest": "^27.0.0-alpha.4",
"jest": "^27.4.7",
"jest-serializer-vue": "^2.0.2",
"ts-jest": "^27.1.3",
"vite-jest": "^0.1.4",
"vue-jest": "^5.0.0-alpha.10",
"babel-jest": "^27.4.6",
"vue3-jest": "^27.0.0-alpha.1"
"vue": "^3.0.5",
"vue-tsc": "^0.28.7",
"vite": "^2.4.4",

Example component:

<script setup lang="ts">
const msg = 'Click me'
function click(): string {
  return 'clicked'
}
</script>

<template>
  <button @click="click">{{ msg }}</button>
</template>

Example test:

import { shallowMount } from '@vue/test-utils'

import Test from './Test.vue'

describe('Test', () => {
  const wrapper = shallowMount(Test)

  test('Component Renders', () => {
    expect(wrapper.exists()).toBeTruthy()
  })

  describe('click method', () => {
    test(`to return 'clicked'`, () => {
      expect(wrapper.vm.click()).toEqual('clicked')
    })
  })
})

Here is an example of casting the wrapper as any to resolve the issue but this is something I would like to avoid.

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const wrapper: any = shallowMount(Test)

enter image description here

2 Answers 2

4

<script setup> is closed by default, so click needs to be explicitly exposed with the defineExpose() macro:

<script setup lang="ts">
function click(): string {
  return 'clicked'
}
   👇
defineExpose({ click })
</script>

Exposing click also makes it available on the wrapper vm instance's type.

Sign up to request clarification or add additional context in comments.

5 Comments

If you only want it exposed in the test environment, you could wrap the call with a check on NODE_ENV. It's 'TEST' when testing.
@tony19 how change NODE_ENV for expose only in the test?
@KevinMendez If using Vite, try: if (import.meta.env.MODE === 'test') { defineExpose({ click }) }
@tony19 - Just FYI, wrapping this in an if statement triggers an ESLint error - 'defineExpose' is not defined. Oh, and it also doesn't seem to actually evaluate at runtime, I assume because this is a macro and not an import.
I can confirm that ESLint complains about defineExpose not being defined. So in general, is using defineExpose really the advised solution here?
1

Also, you could do something like this

it('should my Funct do Something', async () => {
  const wrapper = mount(ComponentName, {})

  // call function
  wrapper.vm.$.setupState.functionName()
  await wrapper.vm.$nextTick()

  //expect something here
})

1 Comment

Totally correct! <script setup> syntax stores all the context in setupState. I had problems with Vue 2.7 + <script setup>. There state was held in const state = wrapper.vm._setupState

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.