DEV Community

Jay
Jay

Posted on

Vite Plugin for Vue 3 testing

Alt Text

Why need test?

  • Get an error if we break code
  • Save time (don't need to test manually)
    • It speeds up development because you don’t have to test everything manually after every change.
  • Integrate into build workflow
  • Improve your code

Vite is an opinionated web dev build tool that serves your code via native ES Module imports during dev and bundles it with Rollup for production.

Vue-Test-Utils is a utilities for testing Vue components

Table Of Contents

Getting Started

  • Lets create a folder mkdir vue-testing
  • cd vue-testing then npm init -y
  • Install dependencies
 npm install vue@3.0.0
 npm install vite @vue/test-utils@^2.0.0-beta.5 --save-dev
  • Create ./src/App.vue
 <template>
   <h1>
     Hello world!
   </h1>
 </template>

 <script>
 export default { 
  name: 'App'
 }
 </script>

  • Create ./src/main.js root director
import {createApp} from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
  • Add index.html
 <!DOCTYPE html>
 <html lang="en">
 </head>
   <body>
     <div id="app"></div>
     <script type="module" src="./src/main.js"></script>
   </body>
 </html>
  • Update or Add scripts to your package.json file
  "scripts": {
    "serve": "vite",
    ....
  }
  • Now we can run our application to make sure everything is working.
npm run serve

Adding Test to your application

  • Lets create a folder mkdir test
  • cd test
  • Create ./test/App.spec.js
import { mount } from '@vue/test-utils'
import App from '../src/App.vue'

 describe('App.spec.js', () => {
  it('renders', () => {
    const wrapper = mount(App, { attachTo: '#root' })
    expect(wrapper.html()).to.contain('Hello')
  })
 })
  • Create ./test/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue Testing</title>
  <link href="https://unpkg.com/mocha@4.0.1/mocha.css" rel="stylesheet"/>
  <script src="https://unpkg.com/chai@4.1.2/chai.js"></script>
  <script src="https://unpkg.com/mocha@4.0.1/mocha.js"></script>
  <style>
    #meta > * {
      margin: 20px 50px 0 50px;
      font-weight: 200;
      font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    }

    #meta > h1 { font-size: 2em; }
    #meta > h2 { font-size: 1.5em; }

    #root {
      margin: 20px 50px 0 50px;
    }
  </style>
  <script>
    mocha.setup('bdd')
    window.expect = chai.expect
  </script>
</head>
<body>
  <div id="meta">
    <h2>Mocha HTML Reporter</h2>
    <div>
      <div id="mocha" style="margin: 0;"></div>
    </div>
  </div>

</body>
</html>
  • Add setup
  beforeEach(() => {
    delete window["__VUE_DEVTOOLS_TOAST__"]

    const existingRoot = document.getElementById('root')
    if (existingRoot) {
      existingRoot.innerHTML = ''
      return
    }
    const root = document.createElement('div')
    root.id = 'root'
    document.body.appendChild(root)
  })

  afterEach(() => {
    document.getElementById('root').remove()
  })
  • Load all spec or test files
import('./test/App.spec.js').then(() => {
  mocha.setup({ reporter: 'html' })
  mocha.checkLeaks()
  mocha.run()
})
  • Run the test
npm run serve
  • Go to http://localhost:3000/test
  • Open the browser console, you will have expected error, vite can only process es module, and @babel/parser is commonjs module Alt Text
  • Vite under the hood uses rollup to build Single File Component SFC, so lets use rollup to convert @babel/parser to es module
  • Create a file ./tools/babel-parser.js
export { parse } from '@babel/parser'
  • Create rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'

export default {
  input: './tools/babel-parser.js',
  plugins: [  commonjs(), resolve() ],
  output: {
    file: './node_modules/@babel/parser/index.js',
    format: 'es',
    sourcemap: true
  }
}
  • Also create vite.config.js to create alias for @babel/parser and to use it later
export default {
  alias: {
    '@babel/parser': '@babel/parser/index.js'
  }
}
  • Add npm script to run the rollup
 "scripts": {
    ...
    "optimize": "rollup -c"
  },
  • Run npm run optimize and npm run serve Alt Text

Create a Vite Plugin

  • Create file ./tools/testPlugin.js
import Router from 'koa-router'
import fs from 'aria-fs'

function getTestFiles() {
  const router = new Router()

  return ({ app }) => {

    router.get('/test-files', async (ctx, next) => {
      ctx.body = await fs.globFiles(`./test/**/*.spec.js`, true)
      await next()
    })

    app.use(router.routes())
  }
}

export default function testPlugin() {
  return {
    configureServer: [ getTestFiles() ]
  }
}
  • Install dependencies
npm install koa-router aria-fs --save-dev
  • Update vite.config.js
import testPlugin from './tools/testPlugin'

export default {
  alias: {
    '@babel/parser': '@babel/parser/index.js'
  },
  plugins: [ testPlugin() ]
}
  • Update script in ./test/index.html
(async function() {

  beforeEach(() => {
    delete window["__VUE_DEVTOOLS_TOAST__"]

    const existingRoot = document.getElementById('root')
    if (existingRoot) {
      existingRoot.innerHTML = ''
      return
    }
    const root = document.createElement('div')
    root.id = 'root'
    document.body.appendChild(root)
  })

  afterEach(() => {
    document.getElementById('root').remove()
  })

  const response = await fetch('/test-files')
  const files = await response.json()

  await Promise.all(files.map(file => import(file)))

  mocha.setup({ reporter: 'html' })
  mocha.checkLeaks()
  mocha.run()
})()
  • Let's add another test Button increment, file ./src/Button.vue
<template>
  <div>
    <p>Times clicked: {{ count }}</p>
    <button @click="increment">increment</button>
  </div>
</template>

<script>
  export default {
    name: 'Button',
    data: () => ({
      count: 0,
    }),
    methods: {
      increment() {
        this.count++
      },
    },
  }
</script>
  • And test for our Button, ./tools/Button.spec.js
import { mount } from '@vue/test-utils'
import Button from '../src/Button.vue'

 describe('Button.spec.js', () => {
  it('increments value on click', async () => {
    const wrapper = mount(Button, { attachTo: '#root' })
    const button = wrapper.find('button')

    await button.trigger('click')
    expect(wrapper.find('p').html()).to.contain('Times clicked: 1')
  })
 })
  • Run npm run serve and go to http://localhost:3000/test Alt Text

Top comments (0)