Skip to content

How to use with Nuxt.js #92

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

Closed
rubenmoya opened this issue Aug 26, 2019 · 14 comments
Closed

How to use with Nuxt.js #92

rubenmoya opened this issue Aug 26, 2019 · 14 comments
Labels
question Further information is requested

Comments

@rubenmoya
Copy link

Hello!

I'm trying to use vue-testing-library with Nuxt.js and I'm facing a problem with asyncData not being called.

I've been reading for a while, and some people call wrapper.vm.$options.asyncData() manually, but that feels super dirty to me, also I don't think vm is available in vue-testing-library

Any idea of how can I do it? I ran out of ideas to be honest.

I've seen @afontcu post about testing api calls but it uses the created life cycle method

Thank you!

@afontcu afontcu added the question Further information is requested label Aug 26, 2019
@afontcu
Copy link
Member

afontcu commented Aug 26, 2019

Hi Rubén! This is quite a good question 🤔

I can't think of a way to test a page with asyncData (or fetch), since they are not part of the usual component lifecycle. AFAIK same issue should happen in Vue Test Utils.

Not sure there's a way for us to fix this, to be honest. You could try and use tools such as nuxt.renderRoute as described here.

If you come up with any good idea, please do share it! 🙌

@afontcu afontcu closed this as completed Sep 3, 2019
@rubenmoya
Copy link
Author

To be honest I didn't came up with a good solution, I just went ahead and used Cypress 🤷‍♂

@afontcu
Copy link
Member

afontcu commented Sep 3, 2019

Thanks for the update! Looks like the way I would've followed, too :)

@Martin7mind
Copy link

i did this and it kind of works.. but is there a better solution now?

export const mountWithAsyncData = async (component, options) => {
  let data
  if (component.asyncData) {
    const asyncDataReq = component.asyncData({ app: { $axios: axios } })
    await options.respondToAsyncData()
    data = await asyncDataReq
  }

  return mount(component, {
    data: () => data,
    ...options
  })
}

@damafeez
Copy link

damafeez commented Jul 23, 2020

This is what I came up with, I might end up converting it into a tiny package later.

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

export async function mountWithAsyncData(
  component,
  { asyncDataContext, ...options } = {},
) {
  const initialData = options.data === 'function' ? options.data() : null
  if ('data' in options && initialData === null)
    throw new Error('data should be a function that returns an object')
  if (typeof component.asyncData === 'function') {
    const data = await component.asyncData(asyncDataContext)
    options.data = () => ({ ...(initialData || {}), ...(data || {}) })
  }
  return mount(component, options)
}

notices how it merges asyncData with existing data in options

Then you can use like, assuming your asyncData function returns { myAsyncData,}

 test('asyncData is merged to VM data', async () => {
  const $emit = jest.fn()
  const wrapper = await mountWithAsyncData(Page, {
    asyncDataContext: {
      $axios: {
        get() {
          return {
            data: myAsyncData,
          }
        },
      },
      $route: mockedRoute,
    },
    mocks: {
      $auth: authMock,
    },
  })
  expect(wrapper.vm.myAsyncData).toBe(myAsyncData)
})

@msmsimondean
Copy link

msmsimondean commented Nov 23, 2020

Here's what I've used:

test/nuxtMountUtils.js

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

export async function shallowMountWithFetch(component, options) {
  options.mountFunction = shallowMount
  return await mountWithFetch(component, options)
}

export async function mountWithFetch(
  component,
  { mountFunction, fetchGlobal, fetchMocks, fetchContext, ...options } = {}
) {
  if (!mountFunction) {
    mountFunction = mount
  }
  const wrapper = mountFunction(component, options)
  const fetch = wrapper.vm.$options.fetch
  if (typeof fetch !== 'function') {
    throw new TypeError('fetch should be a function')
  }
  const thisArg = { ...wrapper.vm.$data, ...fetchMocks }
  const originalGlobal = {}
  for (const key of Object.keys(fetchGlobal)) {
    originalGlobal[key] = global[key]
    global[key] = fetchGlobal[key]
  }
  await fetch.apply(thisArg, [fetchContext])
  for (const key of Object.keys(fetchGlobal)) {
    global[key] = originalGlobal[key]
  }
  delete thisArg.$config
  wrapper.setData(thisArg)
  return wrapper
}

Example Usage

import Index from '@/pages/index.vue'
import { mountWithFetch } from '~/test/nuxtMountUtils'

describe('Index', () => {
  const bodyJson = { /* fake response body goes here */ }
  const fetch = jest.fn(() => {
    expect(url).toBe('https://example.com/something')
    return Promise.resolve({
      json: () => Promise.resolve(bodyJson),
    })
  })
  let wrapper
  const createWrapper = async () => {
    wrapper = await mountWithFetch(Component, {
      fetchGlobal: {
        fetch,
      },
    })
  }

@aldarund
Copy link

There should a way to call vm methods in testing-library to properly work with nuxt or it should have something like nuxt option that will call fetch automatically like created

@afontcu
Copy link
Member

afontcu commented Dec 12, 2020

Hi! Thanks for your input

There should a way to call vm methods in testing-library

I don't think this is likely to happen, as it might lead to tests that rely on implementation details.

it should have something like nuxt option that will call fetch automatically like created

This is interesting and worth exploring! Do you have an idea about what the API should look like?

@aldarund
Copy link

@afontcu there basically two hooks that is executed and needed.
fetch and asyncData https://nuxtjs.org/docs/2.x/features/data-fetching

@afontcu
Copy link
Member

afontcu commented Dec 16, 2020

What could the API look like?

render(Component, { fetch, asyncData })

where fetcth and asyncData are functions that we run appropriately?

@aldarund
Copy link

what for pass them ? I mean its look like more what i offered as first option e.g. to call vm method manually but via different syntax.
What i mean by automatic nuxt support is that render should just call fetch automatically if it exist on component and same for asyncData, but for asyncData it should also merge returned result into component data.

@afontcu
Copy link
Member

afontcu commented Dec 17, 2020

Oh right, I see, forget my last post. I have some doubts (does it mean render is going to return a Promise?), but I'm willing to add this to the library. Would you like to open a PR? 🙌

@msmsimondean
Copy link

Here's what I've used for asyncData:

test/nuxtMountUtils.js

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

// See https://github.com/testing-library/vue-testing-library/issues/92
export async function shallowMountWithAsyncData(component, options) {
  options.mountFunction = shallowMount
  return await mountWithAsyncData(component, options)
}

export async function mountWithAsyncData(
  component,
  { mountFunction, asyncDataGlobal, config, route, ...options } = {}
) {
  if (!mountFunction) {
    mountFunction = mount
  }
  const asyncData = component.options.asyncData
  if (typeof asyncData !== 'function') {
    throw new TypeError('asyncData should be a function')
  }
  const originalGlobal = {}
  for (const key of Object.keys(asyncDataGlobal)) {
    originalGlobal[key] = global[key]
    global[key] = asyncDataGlobal[key]
  }
  const data = await asyncData({ $config: config, route })
  for (const key of Object.keys(asyncDataGlobal)) {
    global[key] = originalGlobal[key]
  }
  const wrapper = mountFunction(component, {
    ...options,
    mocks: { $route: route },
    data: () => data,
  })
  return wrapper
}

Example Usage

import Index from '@/pages/index.vue'
import { mountWithAsyncData } from '~/test/nuxtMountUtils'

describe('Index', () => {
  const config = {
    serviceBaseUrl: 'https://example.com/service',
  }
  const route = {
    params: {
      someParam: 'example',
    },
  }
  const bodyJson = { /* fake response body goes here */ }
  const fetch = jest.fn(() => {
    expect(url).toBe('https://example.com/something')
    return Promise.resolve({
      json: () => Promise.resolve(bodyJson),
    })
  })
  let wrapper
  const createWrapper = async () => {
    wrapper = await mountWithAsyncData(Component, {
      asyncDataGlobal: {
        fetch,
      },
      config,
      route,
    })
  }

@vercel-mamiteamcom
Copy link

any update for this? 😬

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

7 participants