jest-nuxt-helper

A suite for creating Jest tests in a nuxt project

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
jest-nuxt-helper
1.3.03 years ago4 years agoMinified + gzip package size for jest-nuxt-helper in KB

Readme

Jest Nuxt Helper

jest-nuxt-helper

A module that seeks to simplify nuxt integration and support unit testing for SSR methods: asyncData, fetch and "anonymous" middleware
installation:
in your nuxt project's root directory:
npm install -d jest-nuxt-helper

Pre-requisites

The easiest way to support Jest OOB is to choose it as your test framework while running create-nuxt-app
the module assumes you are using $axios as your ajax client and has a lot of stubs baked in for that eventuality. Other ajax clients/methodologies can be stubbed manually.
it is also highly recommended that your project uses the following jest vuex setup by @brandonAAskov
in summary: set your jest.config.js as below:
module.exports = {
  globalSetup: "<rootDir>/jest.setup.js", // this line is the only change here
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/$1",
    "^~/(.*)$": "<rootDir>/$1",
    "^vue$": "vue/dist/vue.common.js"
  },
  moduleFileExtensions: ["js", "vue", "json"],
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.js$": "babel-jest",
    ".*\\.(vue)$": "vue-jest"
  },
  collectCoverage: true,
  collectCoverageFrom: ["<rootDir>/**/*.(vue|js)"],
  forceExit: !!process.env.CI // almost every CI platform sets this by default
}

and create a jest.setup.js file in your project's base directory as below:
import { Nuxt, Builder } from "nuxt"
import nuxtConfig from "./nuxt.config"

// these boolean switches turn off the build for all but the store
const resetConfig = {
  loading: false,
  loadingIndicator: false,
  fetch: {
    client: false,
    server: false
  },
  features: {
    store: true,
    layouts: false,
    meta: false,
    middleware: false,
    transitions: false,
    deprecations: false,
    validate: false,
    asyncData: false,
    fetch: false,
    clientOnline: false,
    clientPrefetch: false,
    clientUseUrl: false,
    componentAliases: false,
    componentClientOnly: false
  },
  build: {
    indicator: false,
    terser: false
  }
}

// we take our nuxt config, lay the resets on top of it,
// and lastly we apply the non-boolean overrides
const config = Object.assign({}, nuxtConfig, resetConfig, {
  mode: "spa",
  srcDir: nuxtConfig.srcDir,
  ignore: ["**/components/**/*", "**/layouts/**/*", "**/pages/**/*"]
})

const buildNuxt = async () => {
  const nuxt = new Nuxt(config)
  await new Builder(nuxt).build()
  return nuxt
}

module.exports = async () => {
  const nuxt = await buildNuxt()

  // we surface this path as an env var now
  // so we can import the store dynamically later on
  process.env.buildDir = nuxt.options.buildDir
}

~/test/Example.spec.js

import { AxiosSpy, MockNuxt } from 'jest-nuxt-helper'
import { mount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'

import index from '@/pages/foo/_barId/index'

const localVue = createLocalVue()
  localVue.use(Vuex)
  let store
  let mockNuxt

  beforeAll(async () => {
    // note the store will mutate across tests
    const storePath = `${process.env.buildDir}/store.js`
    const NuxtStore = await import(storePath)

    // to prevent the store overwriting itself (and causing mutation issues) we create it here.
    store = await NuxtStore.createStore()
  })

  beforeEach(async () => {
    mockNuxt = new MockNuxt({
      store,
      $axios: {
        get (url) {
          // the scope of the method is bound to MockNuxt automatically,
          // this allows you to access MockNuxt methods (e.g. .setAxiosSpy etc.)
          // out of the box, all request verbs are stubbed with functions
          // that set the relevant axios spy observer
          // and return promises that resolve empty objects
          const args = arguments
          return new Promise(
            function (resolve, reject) {
              // spy on this call, send its arguments to this.spies.$axios.get
              this.setAxiosSpy(new AxiosSpy('get', args))
              resolve({ data: { example: 'anExample' } })
            }.bind(this))
        }
      },
      params: { barId: 'Bar123' }
    })
  })

  // unit testing an asyncData method is pretty straight forward
  // (same principle for .fetch())
  test('asyncData valid request', async () => {
    // assuming the index's asyncData method calls $axios.get(`/baz/${params.barId}`)
    const asyncData = await index.asyncData(mockNuxt.context)
    const { path } = mockNuxt.getAxiosSpyByType('get')
    expect(asyncData).toEqual({ example: 'anExample' })
    expect(path).toBe('/baz/Bar123')
  })

  // testing code stored withing your app is easy too!
  test('submit invalid password', async () => {
    // assume there's some kind of form on the page that the user can submit that pushes values to a notifications array in the store:
    expect(store.getters.notifications.length).toBe(0)
    const wrapper = await mount(
      await mockNuxt.callServerSideMethods(index),
      { store, localVue })
    wrapper.trigger('submit')
    expect(store.getters.notifications.length).toBe(1)
  })

  // maybe you want to stub the vm.$route object? simple!
  test('submit using $route', async () => {
    // assuming the submission results in a method calling this.$axios.get(`/baz/${params.barId}`)
    const wrapper = await mount(
      await mockNuxt.callServerSideMethods(index),
      {
        store,
        localVue,
        mocks: mockNuxt.mockPlugins({ $route: { params: { barId: 'fooBar1' } } })
      }
    )
    await wrapper.trigger('submit')
    expect(mockNuxt.getAxiosSpyByType('get')).toEqual({
      path: '/baz/fooBar1',
      requestType: 'get'
    })
  })
})
----

API

class MockNuxt

A class that helps to support Nuxt SSR within a Jest context

Methods

constructor ([Object context])
params:
  • Object context - an object containing overrides of any given context property.

methods within the context object argument will automatically have the mockNuxt instance scope injected into them via .bind() allowing the client to access methods such as .setAxiosSpy etc. within functions within objects such as a .$axios stub (to a depth of 1 level; any deeper could interfere with existing objects such as a vuex store)
default context object:
{
  store: {},
  $axios: { // stubs created for get, post, put, patch & delete
    async [$requestVerb] () {
      const args = arguments
      new Promise(function (resolve) {
        this.setAxiosSpy(new AxiosSpy([$requestVerb], args))
        resolve({ data: {} })
      }.bind(this))
     },
     ...
  },
  error (e) { _this.spies.error.push(e) },
  params: {},
  redirect (r) { _this.spies.redirect.push(r) }
}

callServerSideMethods (VueComponent component[, Object dataOverrides])
call the server side functions on the supplied Vue component and then return it
Params:
  • VueComponent component - the Vue/Nuxt component to perform SSR on
  • Object dataOverrides - data to append/override from the return

clearSpies ()
empty the mockNuxt instance's spies object. Replace it with a brand clone.
default spies object:
{
  redirect: [],
  error: [],
  $axios: {
    get: undefined,
    post: undefined,
    put: undefined,
    patch: undefined,
    delete: undefined
  },
  $router: []
}

getAxiosSpyByType (String type)
get the relevant axios spy from the mockNuxt instance's spies object
Params:
  • String type - the request http verb type

default spies object:
{
  redirect: [],
  error: [],
  $axios: {
    get: undefined,
    post: undefined,
    put: undefined,
    patch: undefined,
    delete: undefined
  },
  $router: []
}

mockPlugins ([Object plugins])
globally available variables such as this.$axios to be injected into vue-test-utils mock property (see example above on how to use)
Params:
  • Object plugins - override or mixin additional plugin stubs

methods within the plugin object argument will automatically have the mockNuxt instance scope injected into them via .bind() allowing the client to access methods such as .setAxiosSpy etc. within functions within objects such as a .$axios stub (to a depth of 1 level; any deeper could interfere with existing objects such as a vuex store)
default mock object:
{
  $axios: { // stubs created for get, post, put, patch & delete
    async [$requestVerb] () {
      const args = arguments
      new Promise(function (resolve) {
        this.setAxiosSpy(new AxiosSpy([$requestVerb], args))
        resolve({ data: {} })
      }.bind(this))
     },
     ...
  },
  $nuxt: { error (e) { _this.spies.error.push(e) } },
  $router: { push (v) { _this.spies.$router.push(v) } }
}

setAxiosSpy(String | AxiosSpy obj[, value])
Params:
  • String | AxiosSpy obj - either the request name or AxiosSpy instance
  • Object value - optional custom object if first object was a string

method to set an axios spy within the mockNuxt's spies.$axios object

GETTERS

get errorSpy () { return this.spies.error }
get redirectSpy () { return this.spies.redirect }
get routerSpy () { return this.spies.$router }

class AxiosSpy

A standardised spy class to represent Axios observers

Methods

constructor (String requestType[, Object args])
Params:
  • String requestType - the request verb
  • Object the Array-like argument object of the caller: see MDN for more information

Example of a simple Axios test

Below is a contrived example of a simple axios post request where a "Next" button is clicked firing a POST request
test('checkEmail step success', async () => {
  const wrapper = await mount(
    await mockNuxt.callServerSideMethods(index),
    {
      localVue,
      mocks: mockNuxt.mockPlugins({
        $route: { params: { someId: 'foo.bar.bah' } },
        $axios:
        {
          post () {
            const args = arguments // allow the arguments for axios' post method to be available in the scope of the promis below. 
            return new Promise((resolve, reject) => {
              this.setAxiosSpy(new AxiosSpy('post', args))
              resolve(true)
            })
          }
        }
      })
    })
  const b = wrapper.get('.NextButton button[title="Next"]')

  // e.g. wait for UI events to occur
  await b.trigger('click')
  jest.advanceTimersByTime(300)
  expect(wrapper.vm.loading).toBe(true)

  await flushPromises()
  jest.advanceTimersByTime(300)
  expect(wrapper.vm.loading).toBe(false)

  expect(mockNuxt.getAxiosSpyByType('post')).toEqual({
    body: { forExample: 'whatever the body is you supplied to your axios call' },
    path: '/your/request/url',
    requestType: 'post'
  })
})