jeudi 28 mai 2020

Test vue component with setTimeout function in Jest

I'm trying to test debounce function with setTimeout. This is an input with ajax api call. I tried jest.useFakeTimers() and jest.runAllTimers() but without success. I also tried to run jest.advanceTimersByTime(501) - because my timeout is for 500ms, but still no luck.

Form component:

<template>
  <div>
    <div class="columns">
      <div class="column">
        <b-field :label="`${$t('categories')}`" label-for="categories">
          <b-taginput
            id="categories"
            v-model="categories"
            :data="filteredCategories"
            :autocomplete="true"
            :allow-new="false"
            :open-on-focus="false"
            icon="label"
            @typing="getFiltered"
          >
          </b-taginput>
        </b-field>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import {
  defineComponent,
  reactive,
  ref,
  Ref,
  toRefs
} from '@vue/composition-api'
import CategoriesRepository from '~/repository/categories'

export default defineComponent({
  props: {
    product: {
      type: any,
      default: () => {}
    }
  },
  setup(props, context) {
    const CategoriesAPI = new CategoriesRepository(context.root.$store)
    const timeout: any = null

    const state = reactive({
      categories: [],
      filteredCategories: [],
      // needed to debounce search
      searchTimeout: timeout
    })

    const getCategories = () => {
      CategoriesAPI.all()
        .then((response) => response.json())
        .then((data) => {
          state.categories = data
        })
        .catch(() => {
          state.categories = []
        })
    }
    const getFiltered = (search: string) => {
      clearTimeout(state.searchTimeout)
      // lines above are not reached by code coverage
      state.searchTimeout = setTimeout(() => {
        if (search.length > 0) {
          CategoriesAPI.search(search)
            .then((response) => response.json())
            .then((data) => {
              state.filteredCategories = data
            })
            .catch(() => {
              state.filteredCategories = []
            })
        }
      }, 500)
    }
    return {
      ...toRefs(state),
      getFiltered
    }
  }
})
</script>

and Form.spec.ts:

import { createLocalVue, mount } from '@vue/test-utils'
import CompositionApi from '@vue/composition-api'
import Buefy from 'buefy'
import mockFetch from '@/test'
import Form from '@/components/products/Form.vue'

const localVue = createLocalVue()
localVue.use(CompositionApi)
localVue.use(Buefy)

describe('Categories in product form', () => {
  jest.useFakeTimers()
  test('Test getting categories', async () => {
    const wrapper = mount(Form, {
      localVue
    })
    const fake_categories = ['test', 'food', 'travel']
    // upload fetch
    // it should be called only by getFiltered
    window.fetch = mockFetch(200, fake_categories)
    wrapper.find('#categories').setValue('test')
    wrapper.find('#categories').trigger('change')
    jest.runAllTimers()
    // then I see in code coverage that state.searchTimeout = setTimeout(()... was not called.
  })

and mockFetch:

function mockFetch(status: number, data: any) {
  return jest.fn().mockImplementation(() =>
    Promise.resolve({
      ok: true,
      status: status,
      json: () => data
    })
  )
}

Aucun commentaire:

Enregistrer un commentaire