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

How to use typescript correctly? #255

Closed
kimond opened this issue Dec 12, 2017 · 34 comments

Comments

Projects
None yet
@kimond
Copy link

commented Dec 12, 2017

I'm not sure if this issue belongs to this project. However, I'm using vue-test-utils since the beginning (even when its name was Avoriaz). But I have some issue to use SFC with typescript and Jest. I was wondering if you planned to write more documentation about which test runners or libs we should use to work properly with Typescript + Jest + Vue-test-utils?

Thank you!

@eddyerburgh

This comment has been minimized.

Copy link
Member

commented Dec 24, 2017

@blake-newman would you be able to write a guide on using vue-test-utils with TypeScript?

@RehanSaeed

This comment has been minimized.

Copy link

commented Jan 5, 2018

Would love this. I'm following a blog post by Alex Joverm but failing to get it to work at all.

@kimond

This comment has been minimized.

Copy link
Author

commented Jan 5, 2018

@RehanSaeed the blog post of Alex Joverm doesn't use TypeScript.

@blake-newman

This comment has been minimized.

Copy link
Member

commented Jan 10, 2018

The main issue, is that wrapper.vm is typed as a generic Vue instance. This should probably typed as the component instance in use. So you can correctly use wrapper.vm in knowledge about what properties are available.

Everything is as you would expect, following the guides.

@ktsn

This comment has been minimized.

Copy link
Member

commented Feb 5, 2018

Just following up @blake-newman's explanation, wrapper.vm is typed as Vue because TypeScript cannot know the component type in .vue files - we usually annotate them like this to avoid compilation error https://github.com/Microsoft/TypeScript-Vue-Starter#single-file-components. If the components are written in normal .ts file, wrapper.vm should be inferred correctly.

Vetur (and probably WebStorm) have their own language service which can deal with .vue files but they only affect .vue files. That means components imported in .vue are typed correctly while components imported in .ts are not.

To be available typed components in .ts, we should use TypeScript plugin vue-ts-plugin or generate .d.ts for each .vue file by using vuetype. But they are still experimental and might not be able to be used in practical projects.

I think it would be the best if Vetur supports TypeScript plugin so that we can use its feature in .ts files including type inference for .vue files.

@mikejamesli

This comment has been minimized.

Copy link

commented Feb 12, 2018

I have recently started writing Vue unit tests using Jest + Typescript + Vue test utils, however i'm facing an issue where passing in my component object into the shallow function appears differently when using vue with typescript.

I believe the reason is because it's using the default constructor of vue (vue-shims.d.ts) and not the typed component. As explained by @ktsn.

Is there a way to pass in the component object correctly using typescript?

Source code to reproduce the issue: https://github.com/mikeli11/Vue-Typescript-Jest

Using typescript:

students.vue
image

Student.Test.ts
image

vue1

Without typescript (https://github.com/vuejs/vue-test-utils-jest-example)
vue2

@tlaak

This comment has been minimized.

Copy link

commented Apr 16, 2018

@kimond What does your Jest configuration look like? My setup cannot even resolve the modules in tests. I have ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest" in the config, but importing components from *.vue files in tests only gives a Cannot find module error.

@kimond

This comment has been minimized.

Copy link
Author

commented Apr 16, 2018

@tlaak Here is my jest configuration. However, I did nothing special in order to make it works.

  "jest": {
    "moduleFileExtensions": [
      "ts",
      "js",
      "json",
      "vue"
    ],
    "transform": {
      "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
      ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
      "^.+\\.ts$": "<rootDir>/node_modules/ts-jest/preprocessor"
    },
    "verbose": true
  }

I forgot to mention that I needed to revert to vue-test-utils 1.0.0-beta.12 since I got some issues with vue-test-utils 1.0.0-beta.13. Since I didn't try the latest version of vue-test-utils.

@tlaak

This comment has been minimized.

Copy link

commented Apr 17, 2018

@kimond That looks like the same I have. I noticed that the module resolution fails if I have my tests in the tests/ directory. When I move a test file under src/ it will pass.

@elevatebart

This comment has been minimized.

Copy link
Contributor

commented Apr 17, 2018

@tlaak It seems like your tsconfig.json is not configured correctly. Only the files in includes (and not in excludes) are compiled and tested. Here is mine for reference:

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "strict": true,
    "jsx": "preserve",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "node",
      "jest"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    }
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.vue",
    "tests/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}
@tlaak

This comment has been minimized.

Copy link

commented Apr 17, 2018

@kimond

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "baseUrl": ".",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["dom", "es5", "es2015"],
    "module": "es2015",
    "moduleResolution": "node",
    "noImplicitAny": false,
    "noImplicitReturns": true,
    "outDir": "./built",
    "sourceMap": true,
    "strict": true,
    "strictNullChecks": false,
    "strictPropertyInitialization": false,
    "target": "es5"
  },
  "include": ["src/**/*", "test/**/*"]
}

My imports are working just fine when I'm running the app in browser. I have the baseUrl set to . so I can use paths like import foo from 'src/components/foo', but these are not working in tests either. I need to use relative import paths there.

@kimond

This comment has been minimized.

Copy link
Author

commented Apr 17, 2018

@tlaak
You need to set a moduleNameMapper in your Jest config. Here an example.

  "jest": {
    "moduleNameMapper": {
      "^src/(.*)$": "<rootDir>/frontend/src/$1",
    },
  }

With the config above you will be able to use src/components/foo from your tests.

@tlaak

This comment has been minimized.

Copy link

commented Apr 17, 2018

@kimond Thanks a million! That helped. I think this conversation verifies that proper 'how to' documentation is really needed :)

@eddyerburgh

This comment has been minimized.

Copy link
Member

commented Apr 17, 2018

Yes we definitely need a how to guide. Would you like to help make a guide?

If a TypeScript user would like to write a guide, you can make a PR and I will help you with the process :)

@kimond

This comment has been minimized.

Copy link
Author

commented Apr 17, 2018

I think the guide could take setup parts from Jest, Typescript then Vue-test-utils from their own guide then combine everything into a guide.
Another possibility would be to make a quick guide that refers to each setup guides using simple links then add a complete example for Vue-test-utils.

The second option is quickest but less detailed than the first one.

@elevatebart

This comment has been minimized.

Copy link
Contributor

commented Apr 17, 2018

I would like to give it a go.
Should I try adding a cookbook to Vuejs.org in a Pull Request?

@eddyerburgh

This comment has been minimized.

Copy link
Member

commented Apr 18, 2018

@elevatebart you could make a pull request in this repo, we have a guides section in the docs.

@feizhen

This comment has been minimized.

Copy link

commented Jun 11, 2018

Is there a guide available here?

@eddyerburgh

This comment has been minimized.

Copy link
Member

commented Jul 30, 2018

I've finally had some time to write a guide on using with TypeScript—https://deploy-preview-876--vue-test-utils.netlify.com/guides/using-with-typescript.html.

Le me know what you think, and if there's any information missing that you would like me to add!

@cesalberca

This comment has been minimized.

Copy link
Contributor

commented Jul 30, 2018

If I can add my two cents I would say TypeScript users would benefit more from the guide in the sense of how to use the library, not how to configure it, as Vue CLI already handles that. Some pitfalls to have into consideration from vue-test-utils + TypeScript:

  • Any method, computed or prop of a component must be casted to any when doing assertions with those:
it('foo', () => {
    const wrapper = mount(Component)
    const foundComponent = wrapper.find({ name: 'SomeComponent' })
    foundComponent.vm.$emit('event')
    // We don't know the signature of wrapper.vm, so TS fails when we try to access bar
    expect((wrapper.vm as any).bar).toBeFalsy()
})
  • Usage with Vuex, the typings and whatnot:
describe('actions', () => {
  let store: Store<RootState>

  beforeEach(() => {
    const localVue = createLocalVue()
    localVue.use(Vuex)
    store = new Vuex.Store({ modules: { langs: langsStore } })
  })

  it('should load a default language', async () => {
    store.state.langs.language = 'es'
    store.dispatch('langs/loadDefaultLanguage')
    await flushPromises()
    expect(store.state.langs.language).toEqual('en')
  })
})
  • What to do when testing a submodule of Vuex as it's own store:
describe('mutations', () => {
  let store: Store<GlobalState>

  beforeEach(() => {
    const localVue = createLocalVue()
    localVue.use(Vuex)
    // We need to cast to any :(
    store = new Vuex.Store(globalStore as any)
  })

  it('should change the globlal state loading to true when enable loading mutation is called', () => {
    store.commit(globalMutationTypes.LOADING.ENABLE)
    expect(store.state.loading).toBeTruthy()
  })
})
  • Dynamic imports won't work in tests as expected with ts-jest (kulshekhar/ts-jest#258)
  • Typings of jest.fn()
  • Handle custom properties testing
interface CustomProperties extends CSSStyleDeclaration {
  Color: string
  Size: string
}

describe('Icon', () => {
  snapshotTest({ component: Icon })

  it('should propagate correctly the color of the icon', () => {
    const wrapper = shallowMount(Icon, {
      propsData: {
        name: 'ui-edit',
        color: 'red'
      }
    })

    expect((wrapper.find('.icon').element.style as CustomProperties).Color).toEqual('var(--red)')
  })
})

I would say in general the guide should be focused in how to avoid at all costs casting to any, and when there is no other way. Maybe when the base guide is completed I could add more to it 🙂

@eddyerburgh

This comment has been minimized.

Copy link
Member

commented Jul 30, 2018

@cesalberca I've added everything that I can to the guide, so if you can add more info/pitfalls that would be great!

Any method, computed or prop of a component must be casted to any when doing assertions with those:

That sounds like a problem with our types that we should fix?

@cesalberca

This comment has been minimized.

Copy link
Contributor

commented Jul 31, 2018

Yeah, it can be done @eddyerburgh. Although I don't know where can I make a PR. Perhaps next week 👍

@ivansieder

This comment has been minimized.

Copy link
Contributor

commented Aug 1, 2018

@cesalberca @eddyerburgh for now, I personally have solved it by using the $data property to access data properties, not sure if that could be another option for the docs for now? It doesn't give me any types of course, but that way it can be avoided to cast every wrapper.vm as any, as each property of wrapper.vm.$data is any by default Record<string, any>

@RehanSaeed

This comment has been minimized.

Copy link

commented Aug 17, 2018

@cesalberca Why do we have to do this:

// We don't know the signature of wrapper.vm, so TS fails when we try to access bar
expect((wrapper.vm as any).bar).toBeFalsy()

Both mount<T> and shallowMount<T> have the generic argument T which is the type of the component, so the vm property should give us intellisense of all properties of the component but this does not actually work.

@chenxeed

This comment has been minimized.

Copy link

commented Sep 22, 2018

@RehanSaeed yes I expect it should be,

but currently I still got error on the TSLint of my VSCode:

screen shot 2018-09-22 at 7 10 51 pm

Could you help clarify if this happened to you as well?

@ivansieder

This comment has been minimized.

Copy link
Contributor

commented Sep 22, 2018

Hi @chenxeed,

as Blake and a few others explained above, it's currently not possible to infer the type of wrapper.vm, as it has a type of Vue, but to make it work, it must be able to somehow infer the type from inside the .vue component, which is currently not possible.

Yesterday after Vue.js London, I had the pleasure to talk to @DanielRosenwasser and he also confirmed, that currently the best ways are most probably to either use type assertion (wrapper.vm).msg or to use vm.$data. (because vm.$data is typed as any).

Hope this clears it up a bit further.

@wilsunson

This comment has been minimized.

Copy link

commented Oct 26, 2018

Hi @chenxeed,

as Blake and a few others explained above, it's currently not possible to infer the type of wrapper.vm, as it has a type of Vue, but to make it work, it must be able to somehow infer the type from inside the .vue component, which is currently not possible.

Yesterday after Vue.js London, I had the pleasure to talk to @DanielRosenwasser and he also confirmed, that currently the best ways are most probably to either use type assertion (wrapper.vm).msg or to use vm.$data. (because vm.$data is typed as any).

Hope this clears it up a bit further.

Hi @ivansieder,
I also meet this kind of problem,it is about wrapper.vm.(methods),i want to test my methods in example.spec.ts,it tells the same issue
907 2 c_gjtqhs pzy_ kl,
but it can work in example.spec.js
it confused me about 3 days...why vm couldn't get a attribute like $methods..

@wilsunson

This comment has been minimized.

Copy link

commented Oct 26, 2018

Hi @chenxeed,
as Blake and a few others explained above, it's currently not possible to infer the type of wrapper.vm, as it has a type of Vue, but to make it work, it must be able to somehow infer the type from inside the .vue component, which is currently not possible.
Yesterday after Vue.js London, I had the pleasure to talk to @DanielRosenwasser and he also confirmed, that currently the best ways are most probably to either use type assertion (wrapper.vm).msg or to use vm.$data. (because vm.$data is typed as any).
Hope this clears it up a bit further.

Hi @ivansieder,
I also meet this kind of problem,it is about wrapper.vm.(methods),i want to test my methods in example.spec.ts,it tells the same issue
907 2 c_gjtqhs pzy_ kl,
but it can work in example.spec.js
it confused me about 3 days...why vm couldn't get a attribute like $methods..

oh yearh,after i waitup,i know how to do:
tim 20181026151852
just let wrapper.vm as any...
finally,what i realize is that:a good sleeping nap can clear up my mine

@iliyaZelenko

This comment has been minimized.

Copy link

commented Feb 24, 2019

Add to your types.d.ts:

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

declare module '@vue/test-utils' {
  interface Wrapper  {
    readonly vm: any
  }
}
@chenxeed

This comment has been minimized.

Copy link

commented Feb 25, 2019

Thank you @ivansieder for the confirmation, that really helps to clarify the current states.

Is there any roadmap that includes this to make this happened? Since in vue 3.0 most likely it'll support TS properly and the newly written components should able to have proper TS typing as well.

@hitochan777 hitochan777 referenced this issue Feb 27, 2019

Merged

Add test #18

@kamok

This comment has been minimized.

Copy link

commented Apr 17, 2019

Add to your types.d.ts:

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

declare module '@vue/test-utils' {
  interface Wrapper  {
    readonly vm: any
  }
}

This "works" as in my IDE is not blowing up, but only patches the problem. I wish this Issue was not closed. Perhaps this question can be moved to another thread?

@jayporta

This comment has been minimized.

Copy link

commented May 8, 2019

Update 5/8/2019
I got around this issue another way by using sinon instead of jest and declaring wrapper as "any"

describe('ComponentToTest.vue', () => {
  let wrapper: any

  beforeEach(() => {
    wrapper = shallowMount(QuotesFind, { localVue })
  })

  afterEach(() => { sinon.restore() })

  it('does not throw TypeScript errors', () => {
    const spy = sinon.spy(wrapper.vm, 'myComponentMethod')
    spy({ message: 'finally...')
    expect(wrapper.vm.$data.myReaction).toBe('yesssss')
  })
})

Original message

Add to your types.d.ts:

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

declare module '@vue/test-utils' {
  interface Wrapper  {
    readonly vm: any
  }
}

I created types.d.ts with this code and it doesn't work.

I get two ts erros:

All declarations of 'Wrapper' must have identical type parameters.

Subsequent property declarations must have the same type. Property 'vm' must be of type 'V', but here has type 'any'.

If I use wilsunson's suggested jest.spyOn((wrapper.vm as any), 'methodName')) results in this error:

Cannot invoke an expression whose type lacks a call signature. Type 'SpyInstance<any, unknown[]>' has no compatible call signatures.

I've tried testing methods in class components and using Vue.extend. Neither work.

Does anyone else have any ideas? I've been at this on and off for a few days now.

@garyo

This comment has been minimized.

Copy link
Contributor

commented May 9, 2019

Like @jayporta , I also can't use the types.d.ts solution, I get the same errors. For data, I can use wrapper.vm.$data, but for computed props and methods, the only thing that seems to work is (wrapper.vm as any).computedProp.
I'd love to see a proper solution to this!
(I'm using jest@24.8.0, ts-jest@24.0,2, typescript@3.4.5, @vue/test-utils@1.0.0-beta.29, Vue class components)

@IlCallo

This comment has been minimized.

Copy link

commented Jul 18, 2019

Maybe you are interested in this finding vuejs/vue-jest#188
And the corresponding wannabe-guide to setup Vue + TypeScript + Jest quasarframework/quasar-testing#48 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.