Lucas
Lucas

Reputation: 14969

Unit test a typescript vue component

I need to be able test my component (methods, computed properties, data, ...). However, when I import my vue component in my unit test:

import Pagination from 'src/components/shared/pagination.vue'
import { newComponent } from '../component-factory'

describe('pagination.vue', () => {
    const propsData = {
        metadata: {
            page: 2,
            records: 155,
            total: 11,
        },
        perPage: 15,
    }

    it('should have correct number of records', () => {
        const ctor = Vue.extend(Pagination)
        const vm = new ctor({propsData}).$mount()
        expect(vm.firstRecord).toBe(16)
        expect(vm.lastRecord).toBe(30)
    })
...

vm is of type Vue, and thus does not have the firstRecord/lastRecord properties. Running the test with karma shows a success, but the typescript compiler spits out Errors:

ERROR in ./tests/shared/pagination.spec.ts
(16,19): error TS2339: Property 'firstRecord' does not exist on type 'Vue'.

ERROR in ./tests/shared/pagination.spec.ts
(17,19): error TS2339: Property 'lastRecord' does not exist on type 'Vue'.

I tried casting:

...
        const vm = new ctor({propsData}).$mount() as Pagination
...

But that results in a warning in VSCode:

[ts] Cannot find name 'Pagination'.

And has the effect of treating vm as type any which is totally counterproductive.

I think this all stems from the fact that when using .vue files you have to add the declaration:

declare module '*.vue' {
    import Vue from 'vue'
    export default typeof Vue
}

Which clearly sets the type of all .vue files to Vue, which isn't exactly a lie, but isn't helpful either... Any suggestions? What am I doing wrong?

For future reference, I have attempted to use vuetype which generates .d.ts files for each .vue file, but ran into this issue. Also, there is a request to make .vue a first class citizen in the typescript ecosystem, which would eliminate this problem. And, I just added a request for a vue language service extension

Upvotes: 1

Views: 2066

Answers (1)

Sean Ray
Sean Ray

Reputation: 1067

Up until Vue 2.5, their TypeScript documentation page recommended exporting an interface that extends Vue if you were not going to use vue-class-component. You can export this interface to use in your tests, to cast your component instance. The recommendation has been removed from the docs, but I have not been able to figure out how to change my tests to not need the interface.

It looks like vuetype could generate these interfaces for you, but I've just been creating them manually.

Here is a greatly simplified example, but you can define anything in your interface that you would reference on vm, ie data, props, methods:

// NOTE: Make sure your interface extends `Vue`!
export interface PaginationComponent extends Vue {
  firstRecord: number,
  lastRecord: number
}

export default {
  name: 'Pagination',
  data: function() {
    return {
      firstRecord: 16,
      lastRecord: 30,
    }
  }
}

For your test, you can cast the component instance to the type of your exported interface:

import Pagination, {PaginationComponent} from 'src/components/shared/pagination.vue'

describe('pagination', () => {
  it('should know about component data fields', () => {
    const ctor = Vue.extend(Pagination)
    const vm : PaginationComponent = new ctor().$mount()
    expect(vm.firstRecord).toBe(16)
    expect(vm.lastRecord).toBe(30)
  })
})

Upvotes: 1

Related Questions