cypress
cypress copied to clipboard
Not able to use Component Testing with Vite/Vue after upgrade to TypeScript 5
Current behavior
I am using Vue 3.2.47, Vite 4.3.3, TypeScript 5.0.4 and I get TypeScript error message after upgrade. It seems similar as the issue that was fixed in #25538
Desired behavior
No error message
Test code to reproduce
MyComponent.ts
:
<template>
<span>
<slot name="item" v-bind="{ item: item }"></slot>
</span>
</template>
<script setup lang="ts">
interface Props {
item: any;
}
defineProps<Props>();
</script>
MyComponent.cypress.ts
:
import { mount } from 'cypress/vue';
import { h } from 'vue';
import MyComponent from './MyComponent .vue';
describe('MyComponent ', () => {
it('should render', () => {
mount(MyComponent , {
props: {
item: {
id: 1,
text: 'First element',
}
},
slots: {
item: ({ item }: { item: any }) =>
h('div', {}, `${item.id} - ${item.text}`),
},
});
});
});
Error is:
MyComponent.cypress.ts:7:11 - error TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type '__VLS_WithTemplateSlots<DefineComponent<__VLS_TypePropsToRuntimeProps<Props>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ... 4 more ..., {}>, { ...; }>' is not assignable to parameter of type 'ComponentOptionsWithObjectProps<__VLS_TypePropsToRuntimeProps<Props>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, string[], string>'.
Type '__VLS_WithTemplateSlots<DefineComponent<__VLS_TypePropsToRuntimeProps<Props>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ... 4 more ..., {}>, { ...; }>' is not assignable to type 'ComponentOptionsBase<Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<Props>>> & { [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined; }, ... 10 more ..., string>'.
Types of property 'emits' are incompatible.
Type 'ThisType<void> | (string[] & ThisType<void>) | undefined' is not assignable to type '(string[] & ThisType<void>) | undefined'.
Type 'ThisType<void>' is not assignable to type 'string[] & ThisType<void>'.
Type 'ThisType<void>' is missing the following properties from type 'string[]': length, pop, push, concat, and 31 more.
7 mount(ListInteractive, {
~~~~~~~~~~~~~~~
node_modules/cypress/vue/dist/index.d.ts:1339:18
1339 declare function mount<PropsOptions extends Readonly<ComponentPropsOptions>, RawBindings, D extends {}, C extends ComputedOptions = {}, M extends Record<string, Function> = {}, E extends EmitsOptions = Record<string, any>, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, EE extends string = string>(componentOptions: ComponentOptionsWithObjectProps<PropsOptions, RawBindings, D, C, M, E, Mixin, Extends, EE>, options?: MountingOptions<ExtractPropTypes<PropsOptions> & PublicProps, D>): Cypress.Chainable<{
~~~~~
The last overload is declared here.
Found 1 error in MyComponent.cypress.ts:7
Cypress Version
12.11.0
Node version
v18
Operating System
Windows
Debug Logs
No response
Other
No response
@mbp I am not able to reproduce the same error using those versions of the libraries you mention, but I might not have setup my project the same as you. Please provide a reproducible example of the issue you're encountering. Here are some tips for providing a Short, Self Contained, Correct, Example and our own Troubleshooting Cypress guide.
https://github.com/cypress-io/cypress/pull/26633 may solve the issue.
@warrensplayer the issue can be reproduced by the same repro that is in #25538
So steps to reproduce:
- Clone https://github.com/lmiller1990/cypress-issue-23653
- Update cypress dependency to latest 12.11.0
- Call
npm run build
- confirm it still compiles (TypeScript 4.9) - Now update remaining dependencies to latest versions (find list below)
- Call
npm run build
. Compilation error occurs
> [email protected] build
Type 'ThisType<void> | (string[] & ThisType<void>) | undefined' is not assignable to type '(string[] & ThisType<void>) | undefined'.
Type 'ThisType<void>' is not assignable to type 'string[] & ThisType<void>'.
Type 'ThisType<void>' is missing the following properties from type 'string[]': length, pop, push, concat, and 31 more.
6 cy.mount(HelloWorld, {}) ~~~~~~~~~~
node_modules/cypress/vue/dist/index.d.ts:1339:18
1339 declare function mount<PropsOptions extends Readonly<ComponentPropsOptions>, RawBindings, D extends {}, C extends ComputedOptions = {}, M extends Record<string, Function> = {}, E extends EmitsOptions = Record<string, any>, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, EE extends string = string>(componentOptions: ComponentOptionsWithObjectProps<PropsOptions, RawBindings, D, C, M, E, Mixin, Extends, EE>, options?: MountingOptions<ExtractPropTypes<PropsOptions> & PublicProps, D>): Cypress.Chainable<{
~~~~~
The last overload is declared here.
Found 1 error in src/components/HelloWorld.cy.ts:6
"dependencies": {
"vue": "3.2.47"
},
"devDependencies": {
"@vitejs/plugin-vue": "4.2.1",
"cypress": "12.11.0",
"typescript": "5.0.4",
"vite": "4.3.3",
"vue-tsc": "1.6.3"
}
I don't know how to test if #26633 fixes the issue
Now that #26633 is part of 12.12.0, I tested again with the updated dependency. But it still does not compile. Line numbers changed a little though:
src/components/HelloWorld.cy.ts:6:14 - error TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type '__VLS_WithTemplateSlots<DefineComponent<__VLS_TypePropsToRuntimeProps<{ label: string; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ... 4 more ..., {}>, { ...; }>' is not assignable to parameter of type 'ComponentOptionsWithObjectProps<__VLS_TypePropsToRuntimeProps<{ label: string; }>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, string[], string>'.
Type '__VLS_WithTemplateSlots<DefineComponent<__VLS_TypePropsToRuntimeProps<{ label: string; }>, {}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ... 4 more ..., {}>, { ...; }>' is not assignable to type 'ComponentOptionsBase<Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ label: string; }>>> & { [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined; }, ... 10 more ..., string>'.
Types of property 'emits' are incompatible.
Type 'ThisType<void> | (string[] & ThisType<void>) | undefined' is not assignable to type '(string[] & ThisType<void>) | undefined'.
Type 'ThisType<void>' is not assignable to type 'string[] & ThisType<void>'.
Type 'ThisType<void>' is missing the following properties from type 'string[]': length, pop, push, concat, and 31 more.
6 cy.mount(HelloWorld, {})
~~~~~~~~~~
node_modules/cypress/vue/dist/index.d.ts:1377:18
1377 declare function mount<PropsOptions extends Readonly<ComponentPropsOptions>, RawBindings, D extends {}, C extends ComputedOptions = {}, M extends Record<string, Function> = {}, E extends EmitsOptions = Record<string, any>, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, EE extends string = string>(componentOptions: ComponentOptionsWithObjectProps<PropsOptions, RawBindings, D, C, M, E, Mixin, Extends, EE>, options?: MountingOptions<ExtractPropTypes<PropsOptions> & PublicProps, D>): Cypress.Chainable<{
~~~~~
The last overload is declared here.
Found 1 error in src/components/HelloWorld.cy.ts:6
Thanks for the updated info @mbp, we'll look into this some more.
I'm having the same error on a project of mine Interestingly, it's only impacting one component - but not 2 others, which are similarly simple (the only difference is that the other components also have a computed value, which the "failing" one doesn't. As such, i think that my setup is correct - but something else is messing with that specific component.
for now, i've @ts-expect-error
'ed this ... but having this correct would be great
I was able to confirm this type error still exists when updating dependencies in the sample repo as documented in the comment above here: https://github.com/cypress-io/cypress/issues/26628#issuecomment-1532579168
The issue is with the slot
in the button. If that is removed, then the typing is fine. Look at updating the mount types appropriately in the built-in Vue adaptor in Cypress.
I am just guessing here - but I believe there is chance this could be fixed with latest type file from @vue/test-utils
. Please see related issue, which is also about combination of slots and props: https://github.com/vuejs/test-utils/issues/2054
This fix is coming in @vue/test-utils 2.4.0, but not released yet.
Very similar error, here is my reproduction:
Setup
- cypress
12.17.4
- vue
3.2.41
- typescript
5.0.2
- tailwind
3.2.1
(since my example tests include tailwind) - vuetify
3.3.14
(since my app is based on vuetify and I have a custommount()
function implementation)
package.json
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"test:e2e": "cypress open"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^6.4.2",
"@inertiajs/vue3": "^1.0.0",
"@tailwindcss/forms": "^0.5.3",
"@types/ziggy-js": "^1.3.2",
"@vitejs/plugin-vue": "^4.0.0",
"autoprefixer": "^10.4.12",
"axios": "^1.1.2",
"cypress": "^12.17.4",
"laravel-vite-plugin": "^0.7.5",
"postcss": "^8.4.18",
"tailwindcss": "^3.2.1",
"typescript": "^5.0.2",
"vite": "^4.0.0",
"vue": "^3.2.41",
"vue-tsc": "^1.2.0"
},
"dependencies": {
"vuetify": "^3.3.14"
}
}
InputLabel.vue
<script setup lang="ts">
defineProps<{
value?: string;
}>();
</script>
<template>
<label class="block font-medium text-sm text-gray-700 dark:text-gray-300">
<span v-if="value">{{ value }}</span>
<span v-else><slot /></span>
</label>
</template>
InputLabel.cy.ts
import InputLabel from '../../../resources/js/Components/InputLabel.vue'
describe('<InputLabel />', () => {
it('renders', () => {
cy.mount(InputLabel, {
props: {
value: 'Test Label'
}
})
})
})
Error for InputLabel.cy.ts (sorry it's german)
Der Typ "{ value: string; }" kann dem Typ "VNodeProps & { __v_isVNode?: undefined; [Symbol.iterator]?: undefined; } & Record<string, any> & { [x: number]: unknown; } & { readonly filter?: Prop<unknown, unknown> | { <S extends string>(predicate: (value: string, index: number, array: readonly string[]) => value is S, thisArg?: any): S[]; (predicate: (value: st..." nicht zugewiesen werden.
Der Typ "{ value: string; }" kann dem Typ "{ readonly filter?: Prop<unknown, unknown> | { <S extends string>(predicate: (value: string, index: number, array: readonly string[]) => value is S, thisArg?: any): S[]; (predicate: (value: string, index: number, array: readonly string[]) => unknown, thisArg?: any): string[]; } | null | undefined; ... 22 more ...; r..." nicht zugewiesen werden.
Die Typen der Eigenschaft "toString" sind nicht kompatibel.
Der Typ "() => string" kann dem Typ "string" nicht zugewiesen werden.ts(2322)
PChip.vue (bunch of vuetify components)
<script setup lang="ts">
defineProps<{
value?: string;
}>()
</script>
<template>
<v-container class="pa-4">
<v-chip class="mb-6">
{{ value }}
</v-chip>
<v-text-field label="Label"></v-text-field>
<v-avatar
color="brown"
>
<span class="text-h5">WE</span>
</v-avatar>
<div class="mt-2">
<v-btn color="error" class="mb-2 mr-2">Test</v-btn>
<v-btn color="warning" class="mb-2 mr-2">Test</v-btn>
<v-btn color="success" class="mb-2 mr-2">Test</v-btn>
</div>
</v-container>
</template>
PChip.cy.ts
import PChip from '../../../resources/js/Components/PChip.vue'
describe('<PChip />', () => {
it('renders', () => {
cy.mount(PChip, {
props: {
value: 'Test Chip'
}
})
})
})
Error for PChip.cy.ts
Der Typ "{ value: string; }" kann dem Typ "VNodeProps & { __v_isVNode?: undefined; [Symbol.iterator]?: undefined; } & Record<string, any> & { [x: number]: unknown; } & { readonly filter?: Prop<unknown, unknown> | { <S extends string>(predicate: (value: string, index: number, array: readonly string[]) => value is S, thisArg?: any): S[]; (predicate: (value: st..." nicht zugewiesen werden.
Der Typ "{ value: string; }" kann dem Typ "{ readonly filter?: Prop<unknown, unknown> | { <S extends string>(predicate: (value: string, index: number, array: readonly string[]) => value is S, thisArg?: any): S[]; (predicate: (value: string, index: number, array: readonly string[]) => unknown, thisArg?: any): string[]; } | null | undefined; ... 22 more ...; r..." nicht zugewiesen werden.
Die Typen der Eigenschaft "toString" sind nicht kompatibel.
Der Typ "() => string" kann dem Typ "string" nicht zugewiesen werden.ts(2322)
component.ts
// ***********************************************************
// This example support/component.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Import styles
import '../../../resources/css/app.css'
import 'vuetify/styles'
import '@fortawesome/fontawesome-free/css/all.css'
// Alternatively you can use CommonJS syntax:
// require('./commands')
import { mount } from 'cypress/vue'
import { h } from 'vue'
import { VApp } from 'vuetify/components'
import vuetify from '../../../resources/js/plugins/vuetify'
Cypress.Commands.add('mount', (component, options = {}) => {
options.global = options.global || {}
options.global.plugins = options.global.plugins || []
options.global.plugins.push({
install(app) {
app.use(vuetify)
},
})
return mount(() => {
return h(VApp, [h(component, options.props)])
}, options)
})
// Example use:
// cy.mount(MyComponent)
type MountParams = Parameters<typeof mount>
type OptionsParam = MountParams[1]
declare global {
namespace Cypress {
interface Chainable {
/**
* Helper mount function for Vue Components
* @param component Vue Component or JSX Element to mount
* @param options Options passed to Vue Test Utils
*/
mount(component: any, options?: OptionsParam): Chainable<any>
}
}
}
Results
The components get rendered correctly within cypress but I get the mentioned TS errors. Also, my component is labeled in Cypress as undefined
:
InputLabel.cy.ts
PChip.cy.ts
We had a very similar error, when having a slot in a tested component. In the end we could silence the error by setting strictFunctionTypes: false
in tsconfig.json
. We consider this a workaround rather than a fix though, and it could be nice not to be forced to have a less strict typescript configuration while using cypress and vue :pray:
I am just guessing here - but I believe there is chance this could be fixed with latest type file from
@vue/test-utils
. Please see related issue, which is also about combination of slots and props: vuejs/test-utils#2054This fix is coming in @vue/test-utils 2.4.0, but not released yet.
This still seems to be the issue. Did you manage to find a workaround?
This still seems to be the issue. Did you manage to find a workaround?
I use this ugly workaround:
mount(MyComponent as any, { ... });
It may be the type definitions in cypress/vue
need updating to sync with the latest version of Test Utils. You can see here it is on 2.3.2 still https://github.com/cypress-io/cypress/blob/e0255f99c253ab2cce83d858369b8c6de2e60637/npm/vue/package.json#L21
I think someone needs to make a PR to update that.
Note that cypress/vue bundles Test Utils - it will not slurp up the one in your node_modules
, so you can't just install the latest Test Utils, Cypress must update the one they are using (just need to do the dependency update and wait for the next Cypress release).
Has anything happened so far?
Not sure, check to see if the internal version of Test Utils was updated, if not you could submit a PR! See above post for more info.
Not sure, check to see if the internal version of Test Utils was updated, if not you could submit a PR! See above post for more info.
I am giving this a try right now. Also, the docs might have to be updated or even extended for Vuetify specifically since the example there is not really working for me and my setup rather looks something like this (not that it is not fully TypeScript compliant and still errors at VLayout
etc.):
import { h } from 'vue'
import { mount } from 'cypress/vue'
import { VLayout } from 'vuetify/components'
import vuetify from '@/plugins/vuetify'
type MountParams = Parameters<typeof mount>
type OptionsParam = MountParams[1]
declare global {
namespace Cypress {
interface Chainable {
mount(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: any,
options?: OptionsParam
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Chainable<any>
}
}
}
Cypress.Commands.add('mount', (component, options = {}) => {
options.global = options.global || {}
options.global.plugins = options.global.plugins || []
options.global.plugins.push({
install (app) {
app.use(vuetify)
}
})
return mount(VLayout, {
...options,
slots: {
default: h(component, options.props, {
...options.slots
})
}
})
})
Note that I use VLayout
since the latest version of Vuetify ships changes to the VApp
wrapper that messed with my components (e.g. stretched them etc. due to display: flex
on the v-applciation wrapper element).
Looking into the official Cypress repo, I noticed that they are using pure JS in component.js
instead of TypeScript as it would be in a component.ts
setup. So opening a PR that will fix TS, would also require changing the setup to TS as well.